当我在此page上阅读有关Stack和Heap的内容时,
我有一个问题,如果,就像在页面上给出的示例中,函数将所有局部变量放在堆栈上,堆栈是否实际访问了不同的变量?
因为堆栈通常只能访问顶层,所以它只能访问函数的一个变量
这是否意味着函数的变量存储在堆栈中的结构中?
答案 0 :(得分:7)
与其他任何指针一样,堆栈指针与指针一样,指向正常的标准内存。要访问堆栈的任何区域,只需在指针上添加一个偏移量即可。
如果从C指针的角度考虑它,就有堆栈指针
char *stack_pointer = some_memory;
然后可以将此指针用作普通指针,包括添加偏移以访问堆栈上的特定位置,例如
。*(int *)(stack_pointer + 4) = 5;
我建议你尝试学习汇编程序代码,然后你可以用一些局部变量编写一个非常简单的程序,并将其编译成汇编程序代码并读取它以确切了解它是如何工作的。
答案 1 :(得分:5)
stack semantics
与stack region
(或存储区域)之间经常存在混淆。相同
去堆。同样,“基于堆栈的虚拟机(如JVM和CLR)”的激增误导了非C和C ++程序员认为本机运行时堆栈的工作方式相同。
区分是非常重要的:
大多数体系结构上的堆栈提供O(1)
的随机访问语义。常见的
示例是立即和基本+偏移量寻址模式以及x86中的堆栈和基址指针。
实际堆栈区域以LIFO方式分配,但各个变量是
random accessible, O(1)
。如果你想让堆栈变得巨大,那就可能是。
空间像LIFO堆栈一样分配。变量在堆栈中像数组/向量或绝对地址(指针)一样被访问。
所以,不,在C和C ++中,你不仅限于一个变量。
答案 2 :(得分:3)
您对数据组织和访问有点混淆。在堆栈中,存储器的组织方式使得只能从“顶部”添加或移除新数据。然而,这与访问其他元素的限制无关。这些限制可能存在于某些逻辑堆栈实现中(例如来自C ++ STL的std::stack
),但它们不是必需的。
硬件堆栈实际上更像是固定大小的数组,具有可变数组起始位置(堆栈指针),因此可以通过索引堆栈指针来访问其他元素。与“标准”数组的不同之处在于它可以包含不同大小的元素。
答案 3 :(得分:2)
堆栈框架由几个元素组成,包括:
- 返回地址
程序中完成后返回函数的地址
- 存储本地数据
为局部变量分配的内存
- 参数存储
为函数参数分配的内存
- 堆栈和基本指针
运行时系统用于管理堆栈的指针
堆栈指针通常指向堆栈的顶部。堆栈基指针(帧指针)经常出现并指向堆栈帧内的地址,例如返回地址。该指针有助于访问堆栈帧的元素。这些指针都不是C指针。它们是运行时系统用于管理程序堆栈的地址。如果运行时系统是用C实现的,那么这些指针可能是真正的C指针。
答案 4 :(得分:1)
你想知道的是堆栈框架是如何工作的。
要使用堆栈帧,您必须有几个寄存器指向几个"感兴趣的点"在所述堆栈帧上并修改它们或使用它们指向的位置的偏移量。一个例子是:
main()
即将致电foo()
。 &{34;基指针"指向main()
的基数。注册EBP。到目前为止,main()
已将所有寄存器用于其自己的堆栈帧。现在,如果要在调用后再次使用它们,则需要保存这些寄存器的内容。在调用之后,foo()
将(除其他外)通过为其局部变量分配内存来设置自己的堆栈帧,设置"堆栈指向"将[ESP]寄存到其堆栈帧的顶部,同时保存main()
基址指针的地址,并复制" next指令的内容"注册称为EIP,以便它知道在完成后返回的位置。 foo()
的堆栈帧现在位于main()
的堆栈帧的顶部,堆栈看起来像这样:
[
foo()
保存的注册。][
foo()
的局部变量。][
main()
基指针地址。此处的EBP 积分,并且foo()
完成后,将指向此处存储的地址。[
main()
返回地址(foo()
将返回此地址所指向的位置。)][
foo()
的论据。][
main()
保存的注册。][...]
如您所见,我们可以访问foo()
的参数及其局部变量作为EBP寄存器指向的简单偏移量。如果第一个局部变量是4个字节长,我们将在EBP - 4中找到它。例如。