首先,让我们考虑根据C17标准如何声明函数
main
。C17标准的第6.7.6.3节“函数声明符(包括原型)”中提到:
14 标识符列表仅声明函数的参数标识符。在函数声明符的定义中,如果使用了空列表,则表示该函数没有参数。在函数声明符的非定义部分中使用空列表,则表示没有提供有关参数数量或类型的信息。
也就是说,当函数具有空的
标识符列表时,可以在函数定义中使用空括号。但是相对于函数
main
,标准要求必须使用
参数类型列表来声明它。
根据C语法:
5 如果在声明“T D1”中,D1的形式为
D ( parameter-type-list )
or
D ( identifier-listopt )
如果在声明“T D”中为ident指定的类型是“derived-declarator-type-list T”,那么为ident指定的类型是“derived-declarator-type-list返回未限定版本的T的函数”。
正如所见,参数类型列表可能是可选的。可选的是标识符列表。
进一步(第10页):
10 作为列表中唯一项的类型为void的未命名参数的特殊情况指定函数没有参数。
这意味着没有参数的main函数应该用包含类型为void的未命名参数的参数列表声明。
根据C17标准(5.1.2.2.1程序启动):
1 在程序启动时调用的函数名为main。实现不为此函数声明原型。它应该定义为返回类型为int且没有参数的函数。
int main(void) { }
或者使用两个参数(在这里称为argc和argv,尽管可以使用任何名称,因为它们是在声明它们的函数内部局部的):
int main(int argc, char *argv[]) { }
或等效;10)或以某种其他实现定义的方式。
现在让我们阅读一下详细解释了“等效”一词的脚注10:
10)因此,int可以被定义为int的typedef名称替换,或者argv的类型可以写为char ** argv,等等。
没有提到函数main的函数声明可以用空的标识符列表重写的情况。
标准要求函数main必须使用函数原型进行声明。上面提供的引用中给出了main的这种声明的示例。
来自C17标准(6.9.1函数定义)
语义学
7 在函数定义中,声明符指定了正在定义的函数的名称和其参数的标识符。如果声明符包括参数类型列表,则该列表还指定了所有参数的类型;这样的声明符还用作同一翻译单元中对同一函数的后续调用的函数原型。如果声明符包括标识符列表,参数的类型应在随后的声明列表中声明。无论哪种情况,每个参数的类型都会根据6.7.6.3中的参数类型列表进行调整;结果类型应为完整的对象类型。
请注意,在C标准中,动词“shall”的意思如下:
符合性
1 在本文档中,“shall”被解释为对实现或程序的要求;相反,“shall not”被解释为禁止。
C23标准相对于
main
的声明有什么变化?
没有!如果你阅读C23标准草案的
5.1.2.2.1程序启动部分,你会发现在关于
main
的声明上使用了相同的文本,没有参数。
是的,现在在C23标准中,函数声明中带有空括号的函数声明符具有另一种含义。参数类型列表是可选的。函数声明符中排除了标识符列表。但是
main
的声明满足与C17标准相同的要求。它应该按照C23标准的要求声明,如下所示:
int main( void )
即使脚注与C17标准中的相同,也没有一个词能够像这样声明函数
main
。
int main()
在C23标准的
5.1.2.2.1程序启动部分中。
在C23标准中,与C17标准相同,保留了使用空参数列表声明
main
的形式。
而
@zwol
错误地声称与
main
的声明相关的“标准中的所有代码片段都被视为示例,因此不具有规范性。” C17标准和C23标准的
5.1.2.2.1程序启动部分都对如何声明主函数进行了规范描述。
你应该注意到,这将是从之前的C标准到C23标准对
main
的规范描述的重大变化。而C23标准应该在其描述中反映出这一点,即“就本草案(C23)而言,已应用了哪些文件”。然而,在这份文件清单中,并未提及对函数
main
声明要求的更改。我只找到了以下与之相关的文件:“N2432删除对带有标识符列表的函数定义的支持”。
如果我接受我是错误的,那么可以得出结论,C23标准存在缺陷,因为这一部分是从C17标准转移而来,没有任何改动,但其含义却发生了变化。至少在C23标准中,应该在这一部分添加一个注释或脚注,以阐明与之前的C标准的变化。
main
声明为int main(int argc, char **argv, char **envp)
,其中envp
是一个以NULL
结尾的环境变量定义数组。我刚在我的Linux系统上尝试了一下,它仍然有效。不确定ANSI C对此有何说法。它可能被认为是非标准的。 - undefinedmain
的定义写为int main() { /*code here*/ }
至少从C11开始就等同于int main(void) { /*code here*/ }
。 - undefined-std=c11
时并不是这样处理的。https://godbolt.org/z/5zf1dr1dK显示GCC在`int main(){}定义之后接受
main(123),即使在
-Wall -Wextra -pedantic下也没有警告。Clang会发出警告,但不会报错,消息中说这只是在C2x中才是错误。GCC的
-std=c23`会报错。标准中的这个条款只是在讨论是否有可以访问以读取传递的任何参数的命名变量。否则,GCC和Clang将偏离C11标准,可能是有意为之,但是C23又有什么改变呢? - undefinedint main() {/*...*/}
是否是main
的有效定义"。我在评论中特别强调了定义。请注意,我的评论中也强调了定义。你的评论是关于一个空参数列表的定义是否作为一个原型。在C11中,答案是“不是”。在C23中,答案是“是”。 - undefined_start
)兼容的情况下,希望调用一个接受0或2个参数的主函数。而不是指定义作为声明的方式,这部分在C23之前是不等效的。 - undefined