为什么eax在"离开"之前被清除



我使用编译器资源管理器生成了一些简单的程序。为什么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输出窗格的左下角寻找任何红色,显示警告的数量。点击它打开一个带有警告的新窗格。(

最新更新