将堆栈用于局部变量的想法是啥?

Posted

技术标签:

【中文标题】将堆栈用于局部变量的想法是啥?【英文标题】:What is the idea behind using a stack for local variables?将堆栈用于局部变量的想法是什么? 【发布时间】:2015-01-09 23:00:12 【问题描述】:

众所周知,在 C 语言中,堆栈是所有局部变量所在的位置。堆栈是先进后出的数据结构,这意味着您只能访问最近推入它的内容。所以给出以下代码:

int k = 5;
int j = 3;
short int i;

if (k > j) i = 1;

显然这是无用的代码,没有真正的意义,但我正在努力解决一些问题。

对于 short int i 声明,我假设在堆栈上分配了 2 个字节。对于 4 个字节的 int k 和 int j 分配值 5 和 3。因此堆栈如下所示

----------   <- stack pointer
int i
----------
int k = 5
----------
int j = 3
----------

所以对于 if 语句,您必须弹出 int i 才能得到条件 k 和 j,如果是,int i 去哪里?如果这是 C 处理局部变量的方式,这一切似乎非常耗时且乏味。

那么这实际上是 C 的做法还是我把它搞砸了?

【问题讨论】:

实际上您在堆栈上的项目布局是错误的,因为 i 是一个短整数,而不是整数。并且堆栈在内存中向下增长,而不是向上增长,并且局部变量以相反的顺序放置在堆栈上。无论如何,本地堆栈上的任何值都不会弹出,而是使用堆栈指针的偏移量,因此引用“k”会导致“从 sp[(偏移量到 k)中读取字(某个寄存器)”类似地用于 'j ' 多变的。并且'i'设置为“从(包含k + j结果的寄存器的下半部分)写入半字sp [偏移量到i]通常您可以将堆栈视为带有分区的长数组 【参考方案1】:

堆栈不是堆栈。它仍然是随机访问内存,这意味着您可以在恒定时间内访问任何位置。堆栈规则的唯一目的是为每个函数调用提供其自己的私有内存区域,该函数可以确保没有被其他人使用。

【讨论】:

确实,如果你看一下 C 函数的反汇编,你不会发现局部变量的 push 和 pop,你会发现从基本堆栈指针的一个常量偏移量 - 变量位于除了在随机存取存储器中之外,这是一个非常可预测的地方。例如,在 OP 的示例中加载 k 的指令看起来像 mov eax, [ebp-4] - 堆栈指针本身是函数中的变量,甚至根本不实际用于局部变量。 似乎有多个来源不同意您的观点,这里是其中一个:gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html note “他的堆栈是一个“FILO”(先进后出)数据结构,由以下人员管理和优化CPU 非常接近。”我应该指出我并不是说它使用硬件堆栈指针,但它实际上是一个 FILO 堆栈 看看生成的代码运行gcc -c foo.c然后objdump -d foo.o可能会有所帮助(如果你像我一样讨厌AT&T语法,可以选择将-M intel添加到objdump),你可以看到什么' 实际生成。虽然我猜如果你不会阅读汇编语言,那也无济于事......但缺点是调用指令将返回地址推入堆栈,然后函数使用本地内存旁边的内存,然后返回会再次弹出所有这些东西。所以它有点像一堆数组,而不是一个纯栈。【参考方案2】:

你有点搞砸了。

是的,本地 (auto) 变量通常存储在堆栈中。但是,它们在读取时不会从堆栈中弹出;它们被堆栈指针的偏移量引用。

取以下代码:

x = y + z;

xyz 中的每一个都在堆栈上分配。当编译器生成等效的机器代码时,它会通过一个给定寄存器的偏移量来引用每个变量,有点像:

mov -8(%ebp), %eax   
add -12(%ebp), %eax
mov %eax, -4(%ebp)

在 x86 架构上,%ebp帧指针;堆栈被分解成,其中每个帧包含函数参数(如果有)、返回地址(即函数调用之后的指令的地址)和局部变量(如果有)。在我熟悉的系统上,堆栈“向下”增长到 0,并且局部变量存储在帧指针“下方”(较低地址),因此是负偏移量。上面的代码假定x 位于-4(%ebp),y 位于-8(%ebp)z 位于-12(%ebp)

当函数返回时,所有东西都会从堆栈中弹出1,但之前不会。

编辑

请注意,none 这是 C 语言定义的强制要求。该语言完全不需要使用运行时堆栈(尽管如果没有编译器,编译器就很难实现)。它只是将auto 变量的生命周期定义为从它们的声明结束到其封闭范围的结束。堆栈使这很容易,但这不是必需的。


1。好吧,堆栈和帧指针将被设置为新值;数据将保留在原来的位置,但该内存现在可供其他用途使用。

【讨论】:

【参考方案3】:

调用堆栈一个堆栈,但它并没有像您想象的那样使用。每次从函数进行调用时,返回地址(程序计数器)与局部变量一起被压入堆栈。当每个函数返回时,堆栈被所谓的一个“堆栈帧”弹出,其中包括变量。 每个函数内,内存被视为随机访问。编译器生成了对堆栈上的局部变量进行排序的代码,确切地知道它们与堆栈帧指针的距离,因此不必推送和弹出各个局部变量。

【讨论】:

【参考方案4】:

堆栈的顶部,在英特尔处理器和许多其他处理器上,由存储在 cpu 寄存器中的地址引用,称为 SP,它被复制到基指针,称为 BP;许多机器指令允许由当前 BP 和字节偏移组成的地址表达式。因此,在您的示例中,我的偏移量为 0,j 的偏移量为 -2,k 的偏移量为 -6。

if 将简单地解析为地址 -6(BP) 和 -4(BP) 的内容的比较。实际偏移值可能因实施而异;但是,这是一般的想法......

【讨论】:

以上是关于将堆栈用于局部变量的想法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

C/C++程序中全局变量局部变量堆栈的存储区域介绍

堆栈动态和堆栈动态数组

调试:是不是可以在不进入每个堆栈帧的情况下打印 C 代码的所有局部变量(带有值)?

编译器如何在堆栈上安排局部变量?

为啥要为局部变量保留堆栈空间?

ava堆栈