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

How does Stack memory work Or How are function variables allocated and accessed on the stack

本文关键字:堆栈 变量 访问 函数 分配 或者 工作 内存 何工作      更新时间:2023-10-16

例如,当我在本页上读到有关堆栈和堆的内容时,
我有一个问题,如果像页面上给出的例子一样,一个函数将其所有的局部变量都放在堆栈上,那么堆栈真的会访问不同的变量吗
因为堆栈通常只能访问顶部,所以它只能访问函数的一个变量
这是否意味着函数的变量存储在堆栈上的结构中?

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

如果你从C指针的角度来考虑它,你就得到了堆栈指针

char *stack_pointer = some_memory;

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

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

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

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

区分很重要

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

大多数体系结构上的堆栈提供了O(1)的随机访问语义。普通例如x86中的立即数和基址+偏移寻址模式以及堆栈和基址指针。实际堆栈区域是以后进先出法的方式分配的,但各个变量random accessible, O(1)。如果你想让堆栈变得巨大,它可能是。

空间的分配就像后进先出法堆栈。变量可以像数组/向量一样在堆栈中访问,也可以通过绝对地址(指针)访问。

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

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

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

堆栈框架由几个元素组成,包括:

-返回地址

程序中完成后返回功能的地址

-本地数据存储

为局部变量分配的内存

-参数存储

为功能参数分配的内存

-堆栈和基指针

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

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

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

要使用堆栈帧,必须有几个寄存器指向几个"堆栈";兴趣点";并修改它们或使用它们所指向的位置的偏移。例如:

CCD_ 6即将呼叫CCD_ 7。CCD_ 8的基由";基指针";注册EBP。到目前为止,main()一直在为自己的堆栈帧使用所有寄存器。现在,如果要在调用后再次使用这些寄存器,则需要保存这些寄存器的内容。在调用之后,foo()将(除其他外)通过为其局部变量分配内存来建立其自己的堆栈帧;堆栈指向";被称为ESP的寄存器移到其堆栈帧的顶部,同时保存CCD_;下一指令";注册名为EIP,这样它就知道在完成后返回到哪里。foo()的堆栈帧现在位于main()的堆栈帧之上,堆栈看起来像这样:

[保存了foo()的寄存器。]

foo()的局部变量】

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

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

[foo()的参数。]

[保存了main()的寄存器。]

[…]

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