【问题标题】:How does Stack memory work Or How are function variables allocated and accessed on the stack堆栈内存如何工作或如何在堆栈上分配和访问函数变量
【发布时间】:2014-12-23 13:09:37
【问题描述】:

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

【问题讨论】:

  • 添加了 C 和 C++ 标签,因为这两种语言都直接使用堆和堆栈,并且堆栈标签的条目和关注者很少^^
  • 阅读维基页面cal stack
  • @basile 调用堆栈完全不同。他在谈论记忆

标签: c++ c stack


【解决方案1】:

一个堆栈帧由几个元素组成,包括:

- 退货地址

函数完成后返回的程序地址

- 存储本地数据

为局部变量分配的内存

- 参数存储

为函数参数分配的内存

- 堆栈和基指针

运行时系统用来管理堆栈的指针

堆栈指针通常指向堆栈的顶部。堆栈基指针(帧指针)通常存在并指向堆栈帧内的地址,例如返回地址。该指针有助于访问堆栈帧的元素。这些指针都不是 C 指针。它们是运行时系统用来管理程序堆栈的地址。如果运行时系统是用 C 实现的,那么这些指针可能是真正的 C 指针。

【讨论】:

    【解决方案2】:

    您对数据组织访问有点困惑。在堆栈中,内存的组织方式使得新数据只能从“顶部”添加或删除。然而,这与访问其他元素的限制无关。此类限制可能存在于某些逻辑堆栈实现中(例如 C++ STL 中的std::stack),但它们不是强制性的。

    硬件栈其实更像是一个固定大小的数组,具有可变数组起始位置(栈指针),因此可以通过索引栈指针来访问其他元素。与“标准”数组的区别在于它可以包含不同大小的元素。

    【讨论】:

    • 那么编译器在编译时使用偏移量?
    • @KorayTugay 我对编译器的实际工作原理并没有深入了解,但我猜是的,偏移值应该在编译时确定。
    【解决方案3】:

    你想知道的是堆栈是如何工作的。

    要使用堆栈帧,您必须有几个寄存器指向所述堆栈帧上的多个“兴趣点”并修改它们或使用它们指向的偏移量。一个例子是:

    main() 即将致电foo()main() 的基址由“基址指针”寄存器 EBP 指向。到目前为止,main() 一直将所有寄存器用于自己的堆栈帧。现在,如果要在调用后再次使用它们,则需要保存这些寄存器的内容。调用之后,foo() 将(除其他外)通过为其局部变量分配内存来设置自己的堆栈帧,将名为 ESP 的“堆栈指向”寄存器设置为其堆栈帧的顶部,同时保存 @ 的地址987654326@ 基指针并通过复制名为 EIP 的“下一条指令”寄存器的内容,以便它知道完成后返回的位置。 foo() 的堆栈帧现在位于 main() 的堆栈帧顶部,堆栈看起来像这样:

    [注册foo()saved。]

    [foo().的局部变量]

    [main() 基指针地址。 EBP 指向这里并将在foo() 完成后指向存储在此处的地址。]

    [main() 返回地址(foo() 将返回此地址指向的位置。)]

    [foo().的参数]

    [注册main()saved。]

    [...]

    如您所见,我们可以访问 foo() 的参数及其局部变量,作为 EBP 寄存器指向的简单偏移量。如果第一个局部变量是 4 字节长,我们将在 EBP - 4 处找到它。

    【讨论】:

      【解决方案4】:

      堆栈指针,就像它的名字所暗示的那样,是一个和其他指针一样的指针,它指向普通的标准内存。要访问堆栈的任何区域,您只需向指针添加一个偏移量。

      如果你从 C 指针的角度来考虑,你有堆栈指针

      char *stack_pointer = some_memory;
      

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

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

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

      【讨论】:

      • 但是怎么知道变量的偏移量呢?
      • @KorayTugay 不知道,但是编译器完全控制堆栈并且知道堆栈上的所有内容在哪里,它确实知道它所有内容的偏移量放入堆栈。
      • @JoachimPileborg 所以这一切都在编译时?
      • @MSalters 你的评论是什么意思?
      • @KorayTugay:我之前的评论(现已删除)提到了旧版本的答案。
      【解决方案5】:

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

      区分很重要:

      1. 语义与区域 - 一个并不意味着另一个。 C 和 C++ 堆栈不是 Java / CLR 堆栈。
      2. 仅来自“调用帧”的“基于堆栈的调用帧” - 调用帧不必堆叠

      大多数架构上的堆栈提供O(1) 的随机访问语义。常见的 例如立即数和基址+偏移寻址模式以及 x86 中的堆栈和基址指针。 实际的堆栈区域以 LIFO 方式分配,但各个变量是 random accessible, O(1)。如果您希望堆栈很大,可以。

      空间的分配方式类似于 LIFO 堆栈。变量在堆栈中访问,如数组/向量或绝对地址(指针)。

      所以,不,在 C 和 C++ 中,您一次不限于单个变量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-04
        • 1970-01-01
        • 2012-03-04
        • 2011-05-28
        • 2012-12-06
        • 2019-05-17
        • 2011-10-09
        相关资源
        最近更新 更多