堆栈内存如何工作或如何在堆栈上分配和访问函数变量

时间:2014-10-28 07:42:27

标签: c++ c stack

当我在此page上阅读有关Stack和Heap的内容时, 我有一个问题,如果,就像在页面上给出的示例中,函数将所有局部变量放在堆栈上,堆栈是否实际访问了不同的变量?
因为堆栈通常只能访问顶层,所以它只能访问函数的一个变量 这是否意味着函数的变量存储在堆栈中的结构中?

5 个答案:

答案 0 :(得分:7)

与其他任何指针一样,堆栈指针与指针一样,指向正常的标准内存。要访问堆栈的任何区域,只需在指针上添加一个偏移量即可。

如果从C指针的角度考虑它,就有堆栈指针

char *stack_pointer = some_memory;

然后可以将此指针用作普通指针,包括添加偏移以访问堆栈上的特定位置,例如

*(int *)(stack_pointer + 4) = 5;

我建议你尝试学习汇编程序代码,然后你可以用一些局部变量编写一个非常简单的程序,并将其编译成汇编程序代码并读取它以确切了解它是如何工作的。

答案 1 :(得分:5)

stack semanticsstack region(或存储区域)之间经常存在混淆。相同 去堆。同样,“基于堆栈的虚拟机(如JVM和CLR)”的激增误导了非C和C ++程序员认为本机运行时堆栈的工作方式相同。

区分是非常重要的:

  1. 语义与地区 - 一个并不代表另一个。 C和C ++堆栈不是Java / CLR堆栈。
  2. “基于堆栈的呼叫帧”仅来自“呼叫帧” - 呼叫帧不必堆叠
  3. 大多数体系结构上的堆栈提供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中找到它。例如。