C 中不带参数的函数

ean*_*mos 5 c signature language-lawyer function-prototypes

    \n
  1. 我知道 \xe2\x80\x9c函数声明符中的空列表不是该函数定义的一部分,指定不提供有关参数数量或类型的信息\xe2\x80\x9e [1]

    \n
    // No information about the parameters is supplied.\nint foo();\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 我知道 \xe2\x80\x9c函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数\xe2\x80\x9e [2]

    \n
    // In this case the foo() function has no parameters.\nint foo()\n{\n    // ...\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  4. \n
  5. 我知道 \xe2\x80\x9c类型的未命名参数的特殊情况void作为列表中唯一的项目指定该函数没有参数\xe2\x80\x9e [3]

    \n
    // foo() has no parameters.\nint foo(void);\n\n// bar() has no parameters.\nint bar(void)\n{\n    // ...\n};\n
    Run Code Online (Sandbox Code Playgroud)\n
  6. \n
\n

所以这里有一些问题:

\n
    \n
  1. 合法吗int main() { /* ... */ }?标准规定[4]

    \n
    \n

    程序启动时调用的函数名为main。该实现没有声明该函数的原型。它的返回类型应定义为int且不带参数定义:

    \n
    int main(void) { /* ... */ }\n
    Run Code Online (Sandbox Code Playgroud)\n

    或带有两个参数(此处称为argcargv,但可以使用任何名称,因为它们对于声明它们的函数来说是本地的):

    \n
    int main(int argc, char *argv[]) { /* ... */ }\n
    Run Code Online (Sandbox Code Playgroud)\n

    或同等学历; 或者以其他一些实现定义的方式。

    \n
    \n

    所以,int main() { /* ... */ }相当于int main(void) { /*... */ }?

    \n
  2. \n
  3. 为什么 GCC 允许将参数传递给没有参数的函数?

    \n
    int foo();\n\nint bar()\n{\n    return 42;\n}\n\nint main(void)\n{\n    // Should be OK.\n    foo(13);\n\n    // Should give a compile-time error.\n    bar(1, 12);\n}\n
    Run Code Online (Sandbox Code Playgroud)\n

    但我实际上可以用以下方法编译程序gcc version 10.1.0 (GCC)gcc -std=c17 -Werror -c test.c

    \n
  4. \n
\n
\n

我读过一些相关问题,例如What are the valid signatures for C\'s main() function? ,但他们没有考虑以下标准条款[2]

\n
\n

作为该函数定义一部分的函数声明符中的空列表指定该函数没有参数。

\n
\n

那么,我对这个条款的理解是否正确呢?

\n
\n
    \n
  1. ISO/IEC 9899:2017 \xc2\xa7 6.7.6.3 / 14。
  2. \n
  3. ISO/IEC 9899:2017 \xc2\xa7 6.7.6.3 / 14。
  4. \n
  5. ISO/IEC 9899:2017 \xc2\xa7 6.7.6.3 / 10。
  6. \n
  7. ISO/IEC 9899:2017 \xc2\xa7 5.1.2.2.1 / 1。
  8. \n
\n

zwo*_*wol 3

  1. 根据标准的字面意思,是的。5.1.2.2.1 规定“[main] 应定义为返回类型 int[,] 并且不带参数,或带两个参数 [...]”。 int main() {}是一个具有返回类型int且没有参数的定义,并且 5.1.2.2.1 没有在任何地方说明该定义需要原型化。

    (紧随“并且不带参数”之后显示的代码片段可能看起来要求编写(void),但所有代码片段都被视为示例,因此不规范。实际上,文本中的任何地方都没有这样说,但它在 DR 回复中至少出现过几次;很遗憾我不记得具体数字了,那已经是 15 年前的事了。)

    然而,由于(2),你int main(void)无论如何都应该写。

  2. 第 6.5.2.2 节(函数调用的语义)第 8 段表示“参数的数量和类型不会与不包含函数原型声明符的函数定义中的参数的数量和类型进行比较。” 然而,同一节的第 6 段表示,如果定义和调用点之间实际不匹配,则行为是未定义的。这意味着,当使用参数调用定义为 的函数时,编译器可以发出错误T foo() {},但这也意味着编译器可以发出错误。

    为了向后兼容 C89 之前的代码(通常使用不匹配数量的参数进行函数调用),许多编译器会毫无怨言地接受此类代码。我在 Clang、MSVC 以及 GCC 中都看到过这种行为。较新版本的 Clang 确实会向您发出警告:

     $ clang --version | head -1
     clang version 9.0.1-13
     $ clang test.c
     test.c:14:14: warning: too many arguments in call to 'bar'
         bar(1, 12);
         ~~~      ^
    
    Run Code Online (Sandbox Code Playgroud)

    我不知道这个警告默认持续了多久。

    为了与真正旧的代码向后兼容,应该有人仔细检查 GCC 的所有故意宽大处理,并默认关闭其中许多,但没有人有兴趣为此提供资金,因此它可能永远不会发生。