There is a curious difference between assemblies of a small program, when compiled as a C-program or as a C++-program (for Linux x86-64).
一个小程序的程序集在编译为C程序或编译为c++程序(用于Linux x86-64)时有一个奇怪的区别。
The code in question:
中的代码的问题:
int fun();
int main(){
return fun();
}
Compiling it as a C-program (with gcc -O2
) yields:
将其编译为c程序(使用gcc -O2)可得到:
main:
xorl %eax, %eax
jmp fun
But compiling it as a C++-program (with g++ -02
) yields:
但是将它编译为一个c++程序(使用g++ -02)会得到:
main:
jmp _Z3funv
I find it puzzling, that the C-version initializes the return value of the main-function with 0
(xorl %eax, %eax
).
我感到困惑的是,c版本初始化主函数的返回值为0 (xorl %eax, %eax)。
Which feature of the C-language is responsible for this necessity?
c语言的哪个特性导致了这种必要性?
Edit: It is true that, for int fun(void);
the is no initialization of the eax-register.
编辑:确实,为int fun(void);不是eax寄存器的初始化。
If there is no prototype of fun
at all, i.e.:
如果根本没有乐趣的原型,例如:
int main(){
return fun();
}
then the C-compiler zeros the eax-register once again.
然后c编译器再次将eax寄存器归零。
2 个解决方案
#1
39
In C int fun();
can take any number of arguments, so it may even be a varargs function. In C++ however it means it takes no arguments.
在C int有趣();可以使用任意数量的参数,因此它甚至可能是一个varargs函数。但是在c++中,这意味着它不接受参数。
The x86-64 sysv abi convention demands that the register AL
must contain the number of SSE registers used when invoking a varargs function. You of course pass no argument, so it is zeroed. For convenience the compiler decided to zero the whole eax
. Declare your prototype as int fun(void);
and the xor
shall disappear.
x86-64 sysv abi约定要求寄存器AL必须包含调用varargs函数时使用的SSE寄存器的数量。你当然不会通过任何论证,所以它是0。为了方便起见,编译器决定将整个eax归零。将您的原型声明为int fun(void);xor会消失。
#2
4
Apparently it is a defensive measure, designed for situations when prototype-less fun
function happens to actually be a variadic function, as explained by @Jester's answer.
显然,这是一种防御措施,设计用于当原型不那么有趣的函数实际上是一个可变函数时,正如@Jester的回答所解释的那样。
Note though that this explanation does not hold any water from the point of view of standard C language.
但是请注意,从标准C语言的观点来看,这种解释是站不住脚的。
Since the beginning of standardized times (C89/90) C language explicitly required all variadic functions to be declared with prototype before the point of the call. Calling a non-prototyped variadic function triggers undefined behavior in standard C. So, formally, compilers do not have to accommodate the possibility of fun
being variadic - if it is, the behavior would be undefined anyway.
由于标准化时间(C89/90)的开始,C语言明确要求在调用点之前用prototype声明所有的变量函数。在标准c中,调用一个非原型的变量值函数会触发未定义的行为。因此,正式地说,编译器不需要考虑到将乐趣转化为变量值的可能性。
Moreover, as @John Bollinger noted in the comments, according to the C standard, a non-prototype int fun()
declaration actually precludes further variadic prototype declarations of fun
. I.e. a variadic function cannot be legally pre-declared as a ()
function. That would be another reason why the above non-prototype declaration is sufficient for the compiler to assume that fun
cannot possibly be variadic.
此外,正如@John Bollinger在评论中指出的那样,根据C标准,一个非原型的int fun()声明实际上排除了进一步的可变因素原型声明的乐趣。例如,变量函数不能被合法地预先声明为()函数。这就是为什么上面的非原型声明对于编译器来说是足够的,可以假设乐趣不可能是可变的。
This could actually be a legacy feature, designed to support pre-standard C code, where pre-declaring variadic functions with prototype was not required.
这实际上可能是一个遗留特性,旨在支持标准前C代码,其中不需要使用prototype预先声明变量函数。
#1
39
In C int fun();
can take any number of arguments, so it may even be a varargs function. In C++ however it means it takes no arguments.
在C int有趣();可以使用任意数量的参数,因此它甚至可能是一个varargs函数。但是在c++中,这意味着它不接受参数。
The x86-64 sysv abi convention demands that the register AL
must contain the number of SSE registers used when invoking a varargs function. You of course pass no argument, so it is zeroed. For convenience the compiler decided to zero the whole eax
. Declare your prototype as int fun(void);
and the xor
shall disappear.
x86-64 sysv abi约定要求寄存器AL必须包含调用varargs函数时使用的SSE寄存器的数量。你当然不会通过任何论证,所以它是0。为了方便起见,编译器决定将整个eax归零。将您的原型声明为int fun(void);xor会消失。
#2
4
Apparently it is a defensive measure, designed for situations when prototype-less fun
function happens to actually be a variadic function, as explained by @Jester's answer.
显然,这是一种防御措施,设计用于当原型不那么有趣的函数实际上是一个可变函数时,正如@Jester的回答所解释的那样。
Note though that this explanation does not hold any water from the point of view of standard C language.
但是请注意,从标准C语言的观点来看,这种解释是站不住脚的。
Since the beginning of standardized times (C89/90) C language explicitly required all variadic functions to be declared with prototype before the point of the call. Calling a non-prototyped variadic function triggers undefined behavior in standard C. So, formally, compilers do not have to accommodate the possibility of fun
being variadic - if it is, the behavior would be undefined anyway.
由于标准化时间(C89/90)的开始,C语言明确要求在调用点之前用prototype声明所有的变量函数。在标准c中,调用一个非原型的变量值函数会触发未定义的行为。因此,正式地说,编译器不需要考虑到将乐趣转化为变量值的可能性。
Moreover, as @John Bollinger noted in the comments, according to the C standard, a non-prototype int fun()
declaration actually precludes further variadic prototype declarations of fun
. I.e. a variadic function cannot be legally pre-declared as a ()
function. That would be another reason why the above non-prototype declaration is sufficient for the compiler to assume that fun
cannot possibly be variadic.
此外,正如@John Bollinger在评论中指出的那样,根据C标准,一个非原型的int fun()声明实际上排除了进一步的可变因素原型声明的乐趣。例如,变量函数不能被合法地预先声明为()函数。这就是为什么上面的非原型声明对于编译器来说是足够的,可以假设乐趣不可能是可变的。
This could actually be a legacy feature, designed to support pre-standard C code, where pre-declaring variadic functions with prototype was not required.
这实际上可能是一个遗留特性,旨在支持标准前C代码,其中不需要使用prototype预先声明变量函数。