为什么对 C 函数的无效使用可以正常编译而没有任何警告?

Mar*_*k R 5 c function-call function-declaration function-definition

与 C 相比,我更喜欢 C++,但这个简单的代码示例让我大吃一惊:

int foo() {
    return 3;
}

int main() {
    int x;
    foo(&x);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/4ov9nTzjM

没有警告,没有链接问题,但此代码仍然无效。

当一些初学者用具有这个奇怪问题的代码提出问题时,我偶然发现了这一点。有人在其他网站(非英语网站)上提出了问题,我想向他提供更好的解释为什么它可以编译(并了解一些有关 C 的知识)。如果有人需要的话,他的原始代码。

我怀疑这个sis在某种程度上与隐式函数声明有关,但我不完全确定。为什么编译器不会抱怨foo使用参数调用?

更新:

这是一个程序集

foo:
        push    rbp
        mov     rbp, rsp
        mov     eax, 3
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rax, [rbp-4]
        mov     rdi, rax
        mov     eax, 0
        call    foo
        mov     eax, DWORD PTR [rbp-4]
        leave
        ret
Run Code Online (Sandbox Code Playgroud)

如您所见,x仍未初始化。

Vla*_*cow 3

编译器应该为此程序发出一条消息

int foo() {
    return 3;
}

int main() {
    int x;
    foo(&x);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

因为函数 foo 使用参数调用,尽管其标识符列表为空。所以程序有未定义的行为。

根据C标准*6.7.6.3函数声明符(包括原型)

14 标识符列表仅声明函数参数的标识符。作为该函数定义一部分的函数声明符中的空列表指定该函数没有参数。函数声明符中的空列表不是该函数定义的一部分,指定不提供有关参数数量或类型的信息。

所以该程序无效。

您可以通过以下方式使其成为有效的程序

int foo();

int main() {
    int x;
    foo(&x);
    return x;
}

int foo( int *p ) {
    return 3;
}
Run Code Online (Sandbox Code Playgroud)

尽管编译器可以发出警告,指出p未使用该参数。

在这种情况下,函数声明而不是其定义意味着没有有关参数数量和类型的信息。

与 C++ 中的 C 声明相反

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

相当于

int foo( void );
Run Code Online (Sandbox Code Playgroud)

  • 所以它看起来像是 GCC 中的一个错误。 (2认同)
  • C 标准不要求编译器为此发出诊断。6.7.6.3 14 确实说定义指定函数没有参数,但这并不是问题中的代码违反的规则。该调用具有未定义的行为,因为 6.5.2.2 6 规定“如果参数数量不等于形参数量,则行为未定义。” 但是,这不在约束部分中,因此违反它不需要诊断。这是一个运行时错误。编译器将被允许定义正常执行的调用。 (2认同)