将堆栈用于局部变量背后的想法是什么?

Ant*_*ony 5 c scope local-variables

在许多人都知道的C中,堆栈是所有局部变量所在的位置.堆栈是最后输出数据结构的第一个意味着您只能访问最近推送到其上的内容.所以给出以下代码:

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

if (k > j) i = 1;
Run Code Online (Sandbox Code Playgroud)

显然这是无用的代码,没有任何实际意义,但我试图绕过一些东西.

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

----------   <- stack pointer
int i
----------
int k = 5
----------
int j = 3
----------
Run Code Online (Sandbox Code Playgroud)

所以对于if语句,你必须弹出int i来获得条件k和j,如果是这样,int我去哪里?如果这是C执行局部变量的方式,这一切似乎都非常耗时且乏味.

那么这实际上C是怎么做的,还是我把它全部搞砸了?

Ker*_* SB 7

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


Joh*_*ode 6

你简直把事情搞砸了。

是的,局部 ( auto) 变量通常存储在堆栈中。然而,它们在读取时并不会从堆栈中弹出;而是会被读取。它们通过堆栈指针的偏移量来引用。

采取以下代码:

x = y + z;
Run Code Online (Sandbox Code Playgroud)

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

mov -8(%ebp), %eax   
add -12(%ebp), %eax
mov %eax, -4(%ebp)
Run Code Online (Sandbox Code Playgroud)

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

当函数返回时,所有内容都会从堆栈1中弹出,但不会在此之前。

编辑

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


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


bry*_*cem 5

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