C中没有参数的函数

A function without parameters in C

  1. 我知道“函数声明符中的空列表不是该函数定义的一部分指定没有信息提供了关于参数数量或类型的信息[1]:

    // No information about the parameters is supplied.
    int foo();
    
  2. 我知道“函数声明符中的一个空列表是该函数定义的一部分指定该函数具有无参数[2].

    // In this case the foo() function has no parameters.
    int foo()
    {
        // ...
    }
    
  3. 我知道“类型为 void 的未命名参数的特殊情况作为列表中的唯一项指定 函数具有无参数[3]:

    // foo() has no parameters.
    int foo(void);
    
    // bar() has no parameters.
    int bar(void)
    {
        // ...
    };
    

所以这里有一些问题:

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

    The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

    int main(void) { /* ... */ }
    

    or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

    int main(int argc, char *argv[]) { /* ... */ }
    

    or equivalent; or in some other implementation-defined manner.

    那么,int main() { /* ... */ } 是否等同于 int main(void) { /*... */ }

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

    int foo();
    
    int bar()
    {
        return 42;
    }
    
    int main(void)
    {
        // Should be OK.
        foo(13);
    
        // Should give a compile-time error.
        bar(1, 12);
    }
    

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


我看了一些相关的问题,比如What are the valid signatures for C's main() function?,但是没有考虑到下面的标准条款[2]:

An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters.

那么,我对这个条款的理解正确吗?


  1. ISO/IEC 9899:2017 § 6.7.6.3 / 14.
  2. ISO/IEC 9899:2017 § 6.7.6.3 / 14.
  3. ISO/IEC 9899:2017 § 6.7.6.3 / 10.
  4. ISO/IEC 9899:2017 § 5.1.2.2.1 / 1.
  1. 根据标准的字母,是的。 5.1.2.2.1 说“[main] 应定义为 return 类型的 int[] 并且没有参数,或者有两个参数 [...]”。 int main() {} 是 return 类型 int 且没有参数的定义,5.1.2.2.1 没有任何地方说定义需要原型。

    ("and with no parameters"后面的代码片段看似要求写成(void),但所有代码片段都被认为是示例,因此不规范。它实际上并没有在文本中的任何地方说这个,但它至少在 DR 响应中出现过几次;很遗憾我不记得具体数字,现在已经超过 15 年了。)

    但是,因为(2),你还是应该写int main(void)

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

    为了与 C8​​9 之前的代码向后兼容,这些代码经常 确实 使用不匹配的参数进行函数调用,许多编译器会毫无怨言地接受这样的代码。我已经在 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);
         ~~~      ^
    

    我不知道这个警告在默认情况下持续了多长时间。

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

  1. 本标准对此含糊不清。它并没有真正定义“或等效”的含义。但是,您可能想知道该标准至少在一个代码示例中使用了 int main()。代码示例虽然不规范。

  2. 调用站点没有看到定义。它只看到一个声明。 (草案文件 N2176)标准说

    [6.9.1/7] If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit.

    没有参数类型列表的声明符不能用作以后调用的函数原型,即使它是定义的一部分。

    [6.5.2.2/8] the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator.