关于C/C++栈内存分配

7
在学习C++(和C)时,我对栈分配的工作原理有一些特殊疑问,但我找不到解决方案:
1. 栈分配是否会隐式调用malloc/free函数?如果没有,它如何确保栈分配和堆分配之间没有冲突?
2. 如果是; C++中的栈分配是否隐式调用new/delete?如果是,则为类重载new运算符是否会影响其栈分配?
在VC++中,这产生了令人困惑的结果;但由于VC++并非完全符合标准(或者我听说过),因此我决定在这里提出问题...

我有这个预感的主要原因是因为从未提到分配给堆栈的空间实际上有多少;或者对象的最大大小,这两个问题在评论中已经得到了回答。 - Leafy
7个回答

27

堆栈分配不使用malloc/free之类的东西。它使用一块内存,被称为程序堆栈,它只是一段连续的内存。

有一个特殊的寄存器存储着堆栈的顶部。当在堆栈上创建新对象时,顶部会上升,从而增加堆栈的大小;当一个对象被释放(超出范围)时,顶部会下降,从而减小堆栈的大小。

如果您试图在堆栈上分配一个过大的对象或进入递归太深,那么堆栈的顶部将超过允许的最大大小,这就是所谓的堆栈溢出。

注意: 堆栈增长的实际方向(地址递增或递减)因系统而异,但无论实际方向如何,基本思想都是相同的。


2
因为 Stack Overflow 的存在,这个网站变得更加受欢迎了。 - Suma
顺便说一句,不要过于纠结于堆栈的增加和减少。在x86上,堆栈向下增长以分配空间,并向上增长以释放空间。请参见https://dev59.com/UnRB5IYBdhLWcg3wUFrB及其优秀的被接受的答案 :-) - paxdiablo
1
这是正确的,当然很重要,但对于那些不知道堆栈分配如何工作的人来说并不是那么关键。 - sharptooth
实际上,在x86和x86_64两种情况下,堆栈都是向下增长的。这意味着每次分配时ESP/RSP(堆栈指针)会被减少。您可以通过分配n * char并将其指针转换为void或其他所需内容来创建堆栈分配器。ALLOC(size, T) alloc((char[size*sizeof(T)]{0x00})) void * alloc(char * obj) { return (void *)obj; } - UserX

17

1
实际上,这两个答案是“不”和“不适用” :-) - paxdiablo
这是在学校学到的基本堆栈。在现实生活中,事情更加复杂,堆栈不一定是经典的堆栈结构,而可以交错到堆中。 - Martin York

7

堆栈分配通常是通过alloca()或编译器隐式完成的。一个好的alloca()只需要极少的指令,并且在完成后不需要(也没有必要)释放它。

您可以将由alloca()分配的内存的指针传递给任何其他期望指针的函数/方法。 您绝不能返回由alloca()分配的指针。

以下是使用堆栈分配的优点缺点


6

这里有一个很好的问题:

"它如何确保堆栈分配和堆分配之间没有冲突?"

几乎所有C/C++实现都有单一连续地址空间,因此堆栈和堆分配的内存必须在该空间中共存。

虽然每次堆栈增长和收缩时并不是使用单独的堆分配完成的,但你仍然可以将堆栈视为从堆中分配的单个大块内存。如果堆栈超出了该块的边界,则会发生堆栈溢出(一个引人注目的名称...有人应该以此命名一个网站)。

在多线程程序中,每次线程启动时都必须为其分配新的堆栈,并且当线程死亡时,堆栈可以被释放。对于那些整个堆栈块,使用与malloc/free相同的堆管理进行分配是有意义的。

因此-粗略地说-您可以将堆栈视为一种在堆中共存的对象类型。整个堆栈在线程启动时一次性用malloc分配,然后从其中进行子分配,最后一次性free

在Windows上,你可以(如果你想要冒险)自己调用相同的虚拟内存API来查找堆栈,并强制释放其中的虚拟页面。

整个堆栈在线程启动时一次性分配,然后从中进行子分配,并且最终也会一次性释放,这就解释了为什么没有冲突。 - Leafy
哦,是的,我知道堆栈溢出与堆栈分配有关;只是我不知道怎么回事 >__< - Leafy

5
在C和C++中,有两种内存分配类型:'自动'和'动态'。其中,'自动'类型是指在函数调用的生命周期内创建对象,而'动态'类型则是由运行时提供的某些内存来分配的。
在绝大多数运行时实现中,自动对象是使用操作系统提供的连续堆栈进行分配的。堆栈通常从高值地址开始,并按对象大小递减。动态分配(C中的malloc,C++中的new)使用从操作系统请求的其他一些内存。由于操作系统知道堆栈正在使用的地址,因此不会将相同的地址分配给动态请求。由于动态区域没有顺序,因此通常称为堆。
因此,'堆栈'分配不需要malloc/free。在C++中,自动对象调用构造函数和析构函数,但不调用new或delete,因为new和delete还具有管理动态内存的代码。

1
不,堆栈分配不会调用malloc/free。整个堆栈空间在程序开始时分配。进入每个函数时,堆栈指针会向前移动足够的距离,以在堆栈上为“堆栈帧”留出空间,这将是您的堆栈分配变量所在的位置。

1
请记住,“堆栈分配”是一种实现细节。没有保证自动存储使用堆栈。例如,IBM 的历史机型没有用到堆栈(尽管听说他们的现代机器会使用)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接