我最近遇到了一篇文章“Deep C Secrets”,它讨论了编译器在编译时解析变量的问题。这对于全局变量和静态变量是可能的,因为它们占用空间直到程序结束,但是局部变量在堆栈上获得空间的情况是什么?他们是否在运行时分配空间,如果是,编译器如何跟踪他们的地址?
答案 0 :(得分:2)
嗯,本地的vairables工作方式与全局/静态的不同。
本地vairables在堆栈上“分配”,而堆栈又是系统为运行程序分配的一块内存。 CPU指向的一个“指针”指向该堆栈,称为堆栈指针,并且一些编译器/ CPU“魔术”更新了函数调用的指针。
最后,“堆栈指针”指向每个函数调用的本地内存块,就像一张专用于每个函数调用的纸张,因此该函数可以使用它来记录不是别处可见。因此,编译器并不真正处理局部变量的“地址”,因为这些是在运行时确定的 - 相反,编译器“跟踪”局部变量在该“纸上”的位置。换句话说,局部变量的位置是“相对于堆栈指针”,或保持为“堆栈指针的偏移量”
答案 1 :(得分:0)
它们的地址是相对于堆栈指针(实际上是大多数情况下的基址指针,IIRC),每次执行函数调用时都会移动它(当然除inline
个函数外)。
要读取局部变量,生成的程序集将如下所示:
mov eax, [ebp - 4]
其中-4
是变量相对于ebp
(扩展(32位)基指针)的位置。如果你有两个int
变量(因此有两个32位变量),它们的位置将是-8
和-4
(因为X86架构上32位是4字节宽)。 / p>
如果有疑问,最好的办法是编写一个简短的程序,并将其编译为汇编程序:
gcc -S program.c -o output.S
cat output.S
有关汇编级函数调用的更多信息,请参阅this wikibook。
答案 2 :(得分:0)
你的问题很有趣。我在这里说的一般,但只要我记得,这就是真的。秘诀是编译器在编译时强制所有调用例程和所有被调用的例程遵循相同的协议。这就是使这一切奏效的原因 编译器输出assy代码,以便调用例程将函数参数压入堆栈(可能按照它们列出的顺序)。然后调用例程跳转到被调用例程,将返回地址(函数调用后的语句)留在堆栈上。
编译器为被调用例程提供类似的assy代码,以便它在堆栈中查找这些参数,其顺序与它们被推送的顺序相同。当被调用的例程使用参数完成时,它会将它们从堆栈中弹出;弹出堆栈的返回地址(保留它);将答案(函数返回类型)推送到堆栈(调用例程将查找它),然后跳转到返回地址。
调用例程将答案从堆栈中弹出并开始使用它。
因此可以重新定位这些函数参数的整个过程。
顺便说一下,所有'local'变量(在被调用函数中)也存在于堆栈中,但仅限于被调用函数运行的时间。被调用的函数暂时借用该空间(直到它返回)。只有函数知道那些局部变量的位置。这就是函数返回后局部变量丢失的原因。
希望我没事。