我使用编译器资源管理器生成了一些简单的程序。为什么eax总是在堆栈弹出rbp之前设置为零?退出前清除寄存器是否符合标准?
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
不,这不是标准配置。你不会"清除";寄存器本身,您总是将它们设置为某个值。有时该值为0。
在C++和C99中,在main()
的底部有一个隐含的return 0;
。EAX是所有主流x86调用约定中的返回值寄存器,包括x86-64 System V.Zeroing EAX实现了return 0;
,如果不调用main
,它就不会存在。
(或者,如果你的来源包括一个明确的return 0;
;你忘记在你的问题中包括它,或者甚至把它作为Godbolt链接的一部分链接;使用"共享"按钮制作一个短链接,或者更好地制作一个完整链接。(
在C++中,从没有return
语句的非void
函数的末尾脱落是未定义的行为。隐式在主计数结束时返回0,因此重命名它将使其成为要到达的执行路径的UB,即要调用的函数。
在C中,只有当调用者使用从一个掉到末尾的函数返回值时,这才是UB,因为C的历史:在添加void
之前,它已经存在了一段时间,所以有一些历史代码的函数声明为foo(int x)
,带有隐式int返回值,但实际上它们没有返回任何东西,调用者也没有读取返回值。
因此,无论如何,在不添加返回语句的情况下将int main()
重命名为int foo()
将使您的函数在C++中具有未定义的行为,并且在启用优化的情况下,GCC根本不会为其发出任何指令,甚至不会发出ret
。
(它当然也会打印一个警告,至少使用-Wall
是这样。在处理编译器输出时,你肯定应该启用-Wall
,因为编译器认为值得警告的事情通常与奇怪的asm有关。在Godbolt上,在asm输出窗格的左下角寻找任何红色,显示警告的数量。点击它打开一个带有警告的新窗格。(