在学习C++(和C)时,我对堆栈分配的工作有一些特别的疑问,我无法找到解决方案:
堆栈分配是否隐式调用malloc/free函数?如果不; 它如何确保堆栈分配和堆分配之间没有冲突?
如是; C++中的堆栈分配是否也隐式调用new/delete?如是; 重载一个类的new运算符会影响它的堆栈分配吗?
它在VC++中产生了令人困惑的结果; 但由于VC++并不完全符合标准(或者我听说过),我决定在这里问一下......
堆栈分配不使用malloc/free之类的东西.它使用一段称为程序堆栈的内存,它只是一段连续的内存.
有一个特殊的寄存器存储堆栈的顶部.当在堆栈上创建新对象时,顶部被抬起从而增加堆栈,当对象被解除分配(超出范围)时,顶部被降低从而减少堆栈.
如果您尝试在堆栈上分配太大的对象或者过于深入递归,则顶部将超出堆栈的最大允许大小,这称为堆栈溢出.
注意:堆栈增长的实际方向(增加或减少地址)将因系统而异,但无论实际方向如何,总体思路都是相同的.
第一个问题的答案是否.堆栈根本没有从堆中分配.
您应该首先阅读堆栈和堆的内容和位置以了解基本概念.
堆栈分配通常根据alloca()或由编译器隐式完成.一个做得好的alloca()只需要很少的指令,并且在你完成时没有任何成本(甚至是需要)来释放它.
您可以将指向alloca()分配的内存的指针传递给任何其他需要指针的函数/方法.你必须永远不要返回alloca()分配的指针.
以下是使用堆栈分配的一些优点和缺点.
这里有一个很好的问题:
"它如何确保堆栈分配和堆分配之间没有冲突?"
几乎所有C/C++实现中都有一个连续的地址空间,因此堆栈和堆分配的内存必须在该空间中共存.
虽然每次堆栈增长和收缩时都不会使用单个堆分配,但您仍然可以将堆栈视为从堆分配的单个大块内存.如果堆栈超出了该块的边界,那么我们就会有一个堆栈溢出(引人注目的名字......有人应该在它之后命名一个网站).
在多线程程序中,每次线程启动时,都必须为它分配一个新堆栈,当线程死亡时,可以释放堆栈.并且使用与通过malloc
/ 公开的相同堆管理来分配那些整个堆栈块是有意义的free
.
所以 - 非常接近 - 你可以认为堆栈是一种在堆中共存的对象.malloc
当一个线程启动时,整个堆栈都被一次性完成,然后它被子分配,然后free
一次性得到-d.
在Windows上,您可以(如果您喜欢危险地生活)自己调用相同的虚拟内存API来查找堆栈并强制释放其中的虚拟页面.
在C和C++中,存在两种类型的内存分配"自动",其中对象是在函数调用的生命周期中创建的,而"动态"则是由运行时提供的函数分配的.
在绝大多数运行时实现中,在创建线程时使用操作系统提供的连续堆栈分配自动对象.堆栈通常从高值地址开始,并按对象的大小递减.动态分配(C中的malloc,C++中的new)使用操作系统请求的其他一些内存.由于操作系统知道堆栈正在使用的地址,因此它不会为动态请求分配相同的地址.由于动态区域没有排序,因此通常称为堆.
所以'堆栈'分配不是malloc/free.C++中的自动对象调用构造函数和析构函数,但不是new或delete,因为new和delete也有管理动态内存的代码.