ead*_*ead 23 c c++ assembly x86-64
当编译为C程序或C++程序(对于Linux x86-64)时,小程序的程序集之间存在奇怪的差异.
有问题的代码:
int fun();
int main(){
return fun();
}
Run Code Online (Sandbox Code Playgroud)
将其编译为C程序(带gcc -O2)产生:
main:
xorl %eax, %eax
jmp fun
Run Code Online (Sandbox Code Playgroud)
但将其编译为C++ - 程序(带g++ -02)产生:
main:
jmp _Z3funv
Run Code Online (Sandbox Code Playgroud)
我觉得很困惑,C版本用0(xorl %eax, %eax)初始化主函数的返回值.
C语言的哪个特性对这种必要性负责?
编辑:确实,因为int fun(void);没有初始化eax寄存器.
如果根本没有原型fun,即:
int main(){
return fun();
}
Run Code Online (Sandbox Code Playgroud)
然后C编译器再次将eax寄存器归零.
Jes*_*ter 40
在C中int fun();可以使用任意数量的参数,因此它甚至可以是varargs函数.但是在C++中,它意味着它不需要参数.
x86-64 sysv abi约定要求寄存器AL必须包含调用varargs函数时使用的SSE寄存器的数量.你当然没有参数,所以它是归零的.为方便起见,编译器决定将整体归零eax.宣告你的原型,int fun(void);并将xor消失.
显然,这是一种防御措施,专为无原型fun函数实际上是可变参数函数的情况而设计,正如 @Jester 的答案所解释的那样。
但请注意,从标准 C 语言的角度来看,这种解释没有任何依据。
自标准化时代(C89/90)开始以来,C 语言明确要求所有可变参数函数在调用点之前使用原型进行声明。调用非原型可变参数函数会触发标准 C 中的未定义行为。因此,正式而言,编译器不必考虑以下可能性:fun可变参数的可能性 - 如果是的话,行为无论如何都将是未定义的。
此外,正如 @John Bollinger 在评论中指出的那样,根据 C 标准,非原型int fun()声明实际上排除了fun. 即可变参数函数不能合法地预先声明为函数()。这就是为什么上述非原型声明足以让编译器假设fun不可能是可变参数的另一个原因。
这实际上可能是一个遗留功能,旨在支持预标准 C 代码,其中不需要使用原型预先声明可变参数函数。