如果我不包含标题,为什么在调用函数之前清除 EAX?

car*_*ass 24 c assembly x86-64 calling-convention

在以下 C 代码中:

#include <stdio.h>
int main(void){getchar();}
Run Code Online (Sandbox Code Playgroud)

它产生以下汇编:

main:
        push    rbp
        mov     rbp, rsp
                 # no extra instruction here when header is included
        call    getchar
        mov     eax, 0
        pop     rbp
        ret
Run Code Online (Sandbox Code Playgroud)

但是,如果我不包含stdio.h在文件中,那么它仍然可以编译,但会添加看起来像随机mov eax, 0指令的内容:

在此处输入图片说明

这是编译器资源管理器:https : //godbolt.org/z/3fTcss。这只是“未定义行为”的一部分,还是有特殊原因将之前的指令call getchar添加到那里?

Nat*_*dge 36

如果没有标头,gcc 会提供一个隐式声明,说明getchar您何时使用它,就好像您之前已经声明过一样

int getchar();
Run Code Online (Sandbox Code Playgroud)

(此行为由旧版本的 C 标准保证。当前版本使使用先前未声明的函数成为未定义行为,但 gcc 仍提供旧行为作为扩展。)

此声明不提供有关getchar预期参数类型的信息。(请记住,与 C++ 不同,声明 with()不会将函数声明为不带参数,而是带未指定的参数,让程序员知道函数期望什么并传递正确数量和类型的参数。)就编译器所知,它甚至可能是可变参数,并且根据 x86-64 SysV ABI,可变参数函数期望al用于传递参数的向量寄存器的数量。这里没有使用向量寄存器,所以编译器在调用之前设置al0。(实际上将所有内容归零会更有效,rax因此它会这样做。)

  • C 标准从一开始就规定隐式声明的函数和没有原型声明的函数不能是可变参数。但大多数编译器无论如何都支持它。 (7认同)
  • @Joshua:第一个 C 标准(C89)做到了。你是对的,早期的预标准 C 没有。 (5认同)
  • 当前版本不使用未声明的函数 UB。实现必须发出诊断,并且可以停止编译,或者隐式定义函数并继续,就像在 C89 中一样。我没有查看标准本身,但至少[此处]引用了 C99 基本原理文档(/sf/answers/1255657511/)。 (3认同)