可能重复:
什么时候最好使用堆栈而不是堆,反之亦然?
我已经阅读了一些关于堆与堆栈的其他问题,但它们似乎更关注堆/堆栈的作用,而不是你为什么要使用它们。
在我看来,堆栈分配几乎总是首选,因为它更快(只是移动堆栈指针而不是在堆中寻找可用空间),并且您不必在使用完毕后手动释放分配的内存. 我可以看到使用堆分配的唯一原因是,如果您想在函数中创建一个对象,然后在该函数范围之外使用它,因为堆栈分配的内存在从函数返回后会自动取消分配。
还有其他我不知道的使用堆分配而不是堆栈分配的原因吗?
可能重复:
什么时候最好使用堆栈而不是堆,反之亦然?
我已经阅读了一些关于堆与堆栈的其他问题,但它们似乎更关注堆/堆栈的作用,而不是你为什么要使用它们。
在我看来,堆栈分配几乎总是首选,因为它更快(只是移动堆栈指针而不是在堆中寻找可用空间),并且您不必在使用完毕后手动释放分配的内存. 我可以看到使用堆分配的唯一原因是,如果您想在函数中创建一个对象,然后在该函数范围之外使用它,因为堆栈分配的内存在从函数返回后会自动取消分配。
还有其他我不知道的使用堆分配而不是堆栈分配的原因吗?
有几个原因:
malloc
/calloc
到free
);如果没有灵活的对象生命周期,则几乎不可能编写有用的数据结构,例如二叉树和链表。
最大的是#1。一旦您进入任何类型的并发或 IPC #1 无处不在。即使是最重要的单线程应用程序在没有一些堆分配的情况下也很难设计。这实际上是在伪造 C/C++ 中的函数式语言。
所以我想做一个字符串。我可以在堆上或堆栈上制作它。让我们尝试一下:
char *heap = malloc(14);
if(heap == NULL)
{
// bad things happened!
}
strcat(heap, "Hello, world!");
对于堆栈:
char stack[] = "Hello, world!";
所以现在我将这两个字符串放在各自的位置。后来,我想让它们更长:
char *tmp = realloc(heap, 20);
if(tmp == NULL)
{
// bad things happened!
}
heap = tmp;
memmove(heap + 13, heap + 7);
memcpy(heap + 7, "cruel ", 6);
对于堆栈:
// umm... What?
这只是一个好处,其他人也提到了其他好处,但这是一个相当不错的好处。有了堆,我们至少可以尝试让我们分配的空间更大。有了堆栈,我们就被我们所拥有的东西困住了。如果我们想要增长空间,我们必须预先声明它,我们都知道看到这个很臭:
char username[MAX_BUF_SIZE];
使用堆最明显的理由是当你调用一个函数并且需要返回一些未知长度的东西时。有时调用者可能会将内存块和大小传递给函数,但在其他时候这是不切实际的,特别是如果返回的内容很复杂(例如,指针飞来飞去的不同对象的集合等)。
在很多情况下,大小限制是一个巨大的交易破坏者。堆栈通常以低兆字节甚至千字节来衡量(这是堆栈上的所有内容),而所有现代 PC 都允许您使用几千兆字节的堆。因此,如果您要使用大量数据,则绝对需要堆。
除了手动控制对象的生命周期(您提到过)之外,使用堆的其他原因包括:
例如,您可以分配一个特定大小的数组,该数组只有在运行时才知道。
随着 C99 中 VLA(可变长度数组)的引入,可以在不使用堆的情况下分配固定运行时大小的数组(这基本上是“alloca”功能的语言级实现)。但是,在其他情况下,即使在 C99 中您仍然需要堆。
例如,当您构建二叉树结构时,您无法提前在堆栈上有意义地分配树的节点。您必须使用堆“按需”分配它们。
当你需要一个大的 I/O 缓冲区时,即使是很短的时间(在单个函数内),从堆中请求它而不是声明一个大的自动数组更有意义。
堆栈变量(通常称为“自动变量”)最适合用于您希望始终保持不变且始终很小的事物。
int x;
char foo[32];
都是堆栈分配,这些在编译时也是固定的。
堆分配的最好理由是你不能总是知道你需要多少空间。您通常只有在程序运行后才知道这一点。您可能对限制有所了解,但您只想使用所需的确切空间量。
如果你必须读入一个 1k 到 50mb 的文件,你不会这样做:-
int readdata ( FILE * f ) {
char inputdata[50*1024*1025];
...
return x;
}
这将尝试在堆栈上分配 50mb,这通常会失败,因为堆栈通常限制为 256k。
堆栈和堆共享相同的“开放”内存空间,如果您使用整个内存段,最终将不得不到达它们相遇的点。保持他们每个人使用的空间之间的平衡将在以后分配和取消分配内存的摊销成本中具有较小的渐近值。