关于C / C ++堆栈分配

在学习C ++(和C)时,我对堆栈分配的工作有一些特别的疑问,我无法找到解决方案:

  1. 堆栈分配是否隐式调用malloc / free函数? 如果不; 如何确保堆栈分配和堆分配之间没有冲突?

  2. 如是; C ++中的堆栈分配是否也隐式调用new / delete? 如是; 重载一个类的new运算符会影响它的堆栈分配吗?

它在VC ++中产生了令人困惑的结果; 但由于VC ++并不完全符合标准(或者我听说过),我决定在这里问一下……

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

有一个特殊的寄存器存储堆栈的顶部。 当在堆栈上创建新对象时,顶部被抬起从而增加堆栈,当对象被解除分配(超出范围)时,顶部被降低从而减少堆栈。

如果您尝试在堆栈上分配太大的对象或者过于深入递归,则顶部将超出堆栈的最大允许大小,这称为堆栈溢出。

注意:堆栈增长的实际方向(增加或减少地址) 将因系统而异 ,但无论实际方向如何,总体思路都是相同的。

你的第一个问题的答案是否。堆栈根本没有从堆中分配。

您应该首先阅读堆栈和堆的内容和位置以了解基本概念。

堆栈分配通常根据alloca()或由编译器隐式完成。 一个做得好的alloca()只需要很少的指令,并且在你完成时没有任何成本(甚至是需要)来释放它。

您可以将指向alloca()分配的内存的指针传递给任何其他需要指针的函数/方法。 你必须永远不要返回alloca()分配的指针。

以下是使用堆栈分配的一些优点和缺点 。

在C和C ++中,有两种类型的内存分配“自动”,其中对象是在函数调用的生命周期中创建的,而“动态”则是由运行时提供的函数分配的。

在绝大多数运行时实现中,在创建线程时使用操作系统提供的连续堆栈分配自动对象。 堆栈通常从高值地址开始,并按对象的大小递减。 动态分配(C中的malloc,C ++中的new)使用操作系统请求的一些其他内存。 由于操作系统知道堆栈正在使用的地址,因此它不会为动态请求分配相同的地址。 由于动态区域没有排序,因此通常称为堆。

所以’堆栈’分配不是malloc / free。 C ++中的自动对象调用构造函数和析构函数,但不是new或delete,因为new和delete也有管理动态内存的代码。

这里有一个很好的问题:

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

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

虽然每次堆栈增长和收缩时都不会使用单独的堆分配,但您仍然可以将堆栈视为从堆分配的单个大块内存。 如果堆栈超出了该块的边界,那么我们就会有堆栈溢出(吸引人的名字……有人应该在它之后命名一个网站)。

在multithreading程序中,每次线程启动时,都必须为它分配一个新堆栈,当线程死亡时,可以释放堆栈。 并且使用与通过malloc / free公开的相同堆管理来分配那些整个堆栈块是有意义的。

所以 – 非常接近 – 你可以认为堆栈是一种在堆中共存的对象。 整个堆栈是malloc -ed一次性完成,当一个线程启动时,然后它被子分配,然后它一次性获得free -d。

在Windows上,您可以(如果您喜欢危险地生活) 自己调用相同的虚拟内存API来查找堆栈并强制释放其中的虚拟页面。

不,堆栈分配不会调用malloc / free。 整个堆栈空间在程序开始时分配。 在进入每个函数时,堆栈指针充分前进,以允许堆栈上的空间为“堆栈帧”,这将是堆栈分配变量所在的位置。

Kepp记住“堆栈分配”是一个实现细节。 无法保证堆栈用于自动存储。 例如,IBM的传统大型机没有(虽然我被告知他们更现代的机器)。