我已经用compiler explorer生成了一些简单的程序。为什么在堆栈弹出rbp之前eax总是被设置为零?在退出之前清除寄存器是标准的吗?
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 1
add DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4]
mov DWORD PTR [rbp-8], eax
mov eax, 0 <--------------------------?
pop rbp
ret
- 作为参考,我使用的是x86-64 gcc 12.1*
1条答案
按热度按时间kqlmhetl1#
不,这不是标准的。你不“清除”寄存器本身,你总是把它们设置为某个值。有时这个值是0。
在C和C99中,
main()
的底部有一个隐式的return 0;
。EAX是所有主流x86调用约定中的返回值寄存器,包括x86-64 System V。清零EAX实现了return 0;
,如果函数没有被调用main
,它就不会存在。(Or如果你的源代码包含一个显式的
return 0;
你忘了在你的问题中包括这一点,甚至把它作为你的 lightning 链接的一部分;使用“共享”按钮创建一个短链接或更好的完整链接。)在C中,在没有
return
语句的情况下,从非void
函数的末尾落下是未定义的行为。在main末尾的隐式返回0计数,因此重命名它将使它成为UB,以便到达执行路径,即,以便调用函数。在C语言中,只有当调用者 * 使用 * 一个从函数末尾掉下来的返回值时,它才是UB,这是因为C语言的历史:在添加
void
之前,它已经存在了一段时间,因此历史代码中声明了类似foo(int x)
的函数,并带有隐式int返回值,但实际上它们没有返回任何内容,调用者也没有读取返回值。因此,无论如何,在不添加return语句的情况下将
int main()
重命名为int foo()
,将使您的函数在C++中具有未定义的行为,并且在启用优化的情况下,GCC根本不会为它发出任何指令,甚至不会发出ret
。(It当然,我也会打印一个警告,至少对于
-Wall
是这样的。在处理编译器输出时,您一定要启用-Wall
,因为编译器认为值得警告的事情通常与奇怪的asm有关。在Godbolt上,在asm输出窗格左下角寻找任何红色,显示警告的数量。单击它可以打开一个新的窗格。)