如何在 C++ 中使用堆栈 [关闭]
Posted
技术标签:
【中文标题】如何在 C++ 中使用堆栈 [关闭]【英文标题】:How stack is used in C++ [closed] 【发布时间】:2016-11-12 20:01:17 【问题描述】:我尝试了解 C++ 程序执行期间的内存管理。我知道当一个函数被调用时,一个函数框架被放置在堆栈上。它由该函数的所有局部变量组成。我也知道堆栈是一种数据结构,其组织方式只有顶部的数据可以访问。那么为什么在该函数内的所有行中都可以访问任何局部变量呢?让我们考虑这个例子:
void function(int a, int b) //Stack frame of function is placed on stack
a++; //variable a can be incremented
b++; //variable b can be incremented
a++; //variable a can be incremented again
假设堆栈在第一行代码之后以这种方式组织:
Variable a
----------
Variable b
----------
(...)
所以我假设我可以在函数的第二行访问变量,因为它位于堆栈的顶部,但是接下来如何访问变量 b? 我认为它就像变量 a 被拉出堆栈,因此可以访问变量 b。但是为什么我可以在代码的下一行中到达变量 a 呢?它在上一步中被拉出堆栈。
【问题讨论】:
“我也知道堆栈是一种数据结构,其组织方式只能访问顶部的数据”这是错误的,堆栈是一种仅允许您添加或删除元素的数据结构在顶部。 @tobi303:这取决于我们讨论的堆栈类型。真正的堆栈机器只能访问堆栈顶部的值。只是现代处理器架构不是堆栈机器。 呃,那是……是什么让你这么想?堆栈(通常)仅用于将参数传递给函数。如果您查看生成的汇编代码,首先发生的事情之一是将值从堆栈移动到 CPU 寄存器中。您也可以随时访问堆栈上的任何值。 @UnholySheep:堆栈肯定也是用来保存局部变量的,除非你只有几个(而且你不知道它们的地址):声明一个本地的int x[1000]
,这将在堆栈上。
最重要的是要理解“堆栈”是一个实现细节。 C++ 根本不需要函数调用帧的堆栈,只是碰巧流行的实现会发出以这种方式工作的代码。因此,试图从有关堆栈的事实中推导出 C++ 语义是没有意义的,因为它是倒退的:nothing 关于堆栈对代码的工作方式强制或强加任何要求。
【参考方案1】:
您在上面的示例中混淆了粒度级别。
程序的调用栈不存储变量,它存储调用帧。变量是调用框架中的子对象(构成那里的大部分数据)。您认为“只能访问顶部的数据”是正确的,但是堆栈顶部的单个对象是整个帧,而不是最近的变量,并且它包含当前调用的所有变量作为其子字段。由于该对象是整体可见的,因此所有这些字段也是可见的,因此当前调用的所有局部变量也是如此。
“只有顶部的数据可以访问”的说法成立,因为当一个函数被调用时,调用框架是不可见的,它的任何变量也不可见。同样,所有这些变量只是一个对象的子字段,即前一帧。当当前帧被弹出时,所有这些都同时再次变得可见,因为单个顶部对象作为一个整体变得可见。
这些都与内存管理无关。用于指代内存的“堆栈”意味着完全超出 C++ 语言范围的东西,但嵌套函数调用形成了一种数据结构,像“纯”堆栈一样工作,无论如何内存由实现提供。
【讨论】:
【参考方案2】:定义 C 和 C++ 的标准没有定义“堆栈”或“堆”。对于编译为物理硬件程序的实现来说,将堆栈用于激活帧和局部变量并使用堆进行动态分配是很常见的。但是,相关标准根本不要求(因此不能保证)。
除此之外,您的问题并没有提及您感兴趣的平台。因此,我们不知道它的功能,因此讨论不直接位于顶部的变量如何变得毫无意义堆栈被访问。
但是,我们可以考虑一下 x86 的流行 C 实现如何处理这个示例:在函数的开头(函数的“序言”),帧大小只是简单地添加到 从堆栈指针中减去,然后通过相对于帧的间接寻址(堆栈指针或基基指针)访问局部变量。在函数结束时(函数的“尾声”),堆栈指针恢复为指向返回地址。通常,现代 x86 编译器不会 push
或 pop
“分配”或访问局部变量。
【讨论】:
【参考方案3】:要查看堆栈是如何使用的,请编译代码以进行汇编。您可以使用 g++ -S main.c
使用 g++ 执行此操作。
然后您可以看到堆栈在做什么。
例如,pushq %rbp
移动堆栈指针的位置,movl $1, -12(%rbp)
将一个值(在本例中为值 1)移动到堆栈顶部后面 12 个字节的内存位置,movl -4(%rbp), %eax
移动一个堆栈后面 4 个字节的值存入eax
寄存器。
【讨论】:
以上是关于如何在 C++ 中使用堆栈 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
如何在 c / c++ 程序中检测可能/潜在的堆栈溢出问题?