【问题标题】:A function without parameters in CC中没有参数的函数
【发布时间】:2020-11-11 03:57:10
【问题描述】:
  1. 我知道“不属于该函数定义的函数声明符中的空列表指定不提供有关参数数量或类型的信息 em>„[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]

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

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

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

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

    或等价物;或以其他一些实现定义的方式。

    那么,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]

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

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


  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。

【问题讨论】:

标签: c language-lawyer signature function-prototypes


【解决方案1】:
  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() {} 的函数时,允许编译器发出错误,但这也意味着允许编译器发出错误。

    为了与 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 的所有故意宽大,并默认关闭其中的许多功能,但没有人有兴趣为此提供资金,因此它可能永远不会发生。

【讨论】:

  • 按照标准的字母,是的 尽管T f() { ... }T f(void) { ... } 都定义了一个没有参数的函数,但这些定义并不是100% 等效的:第一个form 不提供函数原型。为什么您确定 C 标准措辞中关于 main 函数的 “或等效项” 忽略了这种差异???
  • 问题中引用的 @LanguageLawyer 5.1.2.2.1 说“[main] 应定义为返回类型为 int[,] 且不带参数,或使用两个参数 [.. .]”。它并没有说定义需要原型。事实上,int main (argc, argv) int argc; char **argv; { ... } 仍然是允许的,因为我阅读了标准。
  • (2) 这不是偏差,我在回答中引用了标准的相关部分。目前尚不清楚它如何影响 (1),因为无论如何您都不应该致电 main
  • 好吧,我明白你的意思了。措辞不准确,但“不带参数”并不一定意味着“带空参数列表”。参数列表由唯一的void 组成的函数也是一个没有参数的函数。
  • @n.'pronouns'm。 “无论如何你都不应该调用 main” 与 C++ 不同,C 中不禁止调用 main
【解决方案2】:
  1. 本标准对此含糊其辞。它并没有真正定义“或等价物”的含义。但是,您可能想知道该标准在至少一个代码示例中使用了int main()。虽然代码示例并不规范。

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

    [6.9.1/7] 如果声明符包含参数类型列表,该列表还指定了所有参数的类型; 这样的声明符还可以作为函数原型,供以后在同一个翻译单元中调用同一个函数。

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

    [6.5.2.2/8] 参数的数量和类型是 不与不包含函数原型声明符的函数定义中的参数进行比较。

【讨论】:

  • 无论是否需要正确处理示例程序的能力以实现一致性,我认为标准的作者显然希望质量实现应该能够有意义地处理他们的示例代码,而无需他们不能的充分理由。此外,该标准对一致性的要求过于宽松,以至于“规范”和“非规范”部分之间的区别基本上没有意义。
猜你喜欢
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-31
相关资源
最近更新 更多