在C和c++中调用函数时,EAX寄存器初始化的差异

时间:2021-12-01 13:38:24

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预先声明变量函数。