非常快速的问题。当我在C中存储一些自动变量时,asm输出是这样的:MOV ESP+4,#25h
,我只是想知道为什么编译器不能计算ESP+4
地址本身。
我想到了这一点,我真的无法找到理由。我的意思是,不是编译器知道esp值吗?它应该是。当使用另一个目标文件时,这也不应该是问题,因为变量可以只是由地址表示并在以后链接,当所有自动变量都已知时,因此可以分配正确的地址。感谢。
答案 0 :(得分:3)
不,编译器在运行时不知道ESP
的值 - 它是堆栈指针。每次调用函数时它都可能不同。也许最简单的例子是递归函数 - 每次调用自身时,堆栈会更深入地适应新调用的局部变量。每个堆栈帧都有自己的局部变量,每个堆栈帧位于堆栈的不同位置,因此有自己的地址(通常在ESP
中)。
答案 1 :(得分:3)
不,它无法事先知道esp
的价值。
以递归函数为例,即。一个自称的函数。假设这样的函数有几个通过堆栈传入的参数。这意味着每个参数在堆栈上占用一些空间,从而改变esp
寄存器的值。
现在,当输入函数时,esp
的确切值将取决于函数先前调用自身的次数,编译器无法在编译时知道这一点。如果你对此表示怀疑,请采取如下函数:
void foobar(int n)
{
if (rand() % n != 17)
foobar(n + 1);
}
编译器无法提前足够聪明地判断该函数是否会再次调用自己。
如果编译器想要提前确定esp
,那么它实际上必须为esp
的每个可能值创建一个函数版本。
以上说明仅考虑一个功能。在现实世界的场景中,程序具有许多彼此相互依赖的功能,这导致相当复杂的“调用图”。这与(以及其他)不可预测的程序逻辑一起意味着编译器必须创建每个函数的大量版本,只是为了优化esp
- 这显然没有意义。
P.S。:现在别的了。实际上你根本不需要优化[esp+N]
,因为它不应该比简单的[esp]
占用更多的CPU时间......至少在英特尔奔腾CPU上不行。你可以说它们已经包含了针对这个甚至更复杂场景的优化。如果您对Intel CPU感兴趣,我建议您查找文档,查找机器指令的 MOD R / M 和 SIB 字节,例如: here for the SIB byte或here,当然还有英特尔官方CPU开发人员文档。
答案 2 :(得分:2)
无法在编译时计算堆栈指针。举一个简单的例子,为什么这是不可能的,只需考虑一个递归函数:同一个变量为每个调用都有不同的地址,但它总是与运行的代码相同。
答案 3 :(得分:2)
不,编译器不会提前知道该值。在一些非常基本的程序中(只有一个可能的“路由”从main
到任何其他被调用的特定函数),可以,但我不知道编译器会尝试计算这个。如果你有任何递归,或者从多个地方调用一个函数,堆栈指针将根据调用它的位置而具有不同的值。
在任何情况下都没有太多意义 - 因为堆栈指针 如此频繁使用,大多数CPU被设计为使堆栈指针的间接寻址非常有效。实际上,它通常比提供绝对地址更有效。
答案 4 :(得分:1)
这对堆栈的工作方式来说非常重要。为了自己解释一下,想象一下如何实现递归函数。