在C和C++中定义main()函数的正确(最有效)方法是什么?int main()或void main()-为什么?那争论呢?如果int main(),则返回1或0?


这个问题有很多重复之处,包括:

C的main()函数的有效签名是什么?main()函数的返回类型void main()和int main()之间的区别?main()在C中的签名++main()的正确声明是什么?-对于C++,确实有一个非常好的答案。C中main()函数的样式C中main()方法的返回类型C中的int main()与void main()

相关:

C++-int main(int argc,char**argv)C++-int main(int argc,char*argv[])char*envp[]作为main()的第三个参数是否可移植?int main()函数必须在所有编译器中返回值吗?为什么C和C++中main()函数的类型留给用户定义?为什么int main(){}编译?C++14中main()的合法定义?


当前回答

省略返回0

当C或C++程序到达main结尾时,编译器将自动生成返回0的代码,因此不需要将返回值设置为0;明确地在main的末尾。

注意:当我提出这个建议时,后面几乎总是两种评论中的一种:“我不知道。”或“这是坏建议!”我的理由是,依赖标准明确支持的编译器行为是安全和有用的。对于C,自C99以来;见ISO/IEC 9899:1999第5.1.2.2.3节:

[…]从初始调用返回到主函数相当于用主函数返回的值作为其参数调用退出函数;到达终止主函数的}返回值0。

对于C++,自1998年第一个标准以来;见ISO/IEC 14882:1998第3.6.1节:

如果控件到达main的结尾而没有遇到return语句,则效果是执行return 0;

从那时起,两种标准的所有版本(C99和C++98)都保持了相同的想法。我们依赖于C++中自动生成的成员函数,很少有人编写显式返回;void函数末尾的语句。反对省略的理由似乎归结为“看起来很奇怪”。如果像我一样,你对C标准更改的原因感到好奇,请阅读这个问题。还要注意,在20世纪90年代初,这被认为是“草率的做法”,因为当时这是一种未定义的行为(尽管得到广泛支持)。

此外,C++核心指南包含省略返回0的多个实例;在main结尾,没有编写显式返回的实例。尽管该文件中还没有关于这一特定主题的具体准则,但这似乎至少是对这一做法的默认认可。

所以我主张省略它;其他人不同意(通常是激烈的!)在任何情况下,如果你遇到省略了它的代码,你会知道它是由标准明确支持的,你也会知道它的含义。

其他回答

注意,C和C++标准定义了两种实现:独立的和托管的。

C90托管环境

允许的表单1:

int main (void)
int main (int argc, char *argv[])

main (void)
main (int argc, char *argv[])
/*... etc, similar forms with implicit int */

评论:

前两个被显式声明为允许的形式,其他的被隐式允许,因为C90允许返回类型和函数参数为“隐式int”。不允许使用其他形式。

C90独立环境

允许使用任何形式或名称的main 2。

C99托管环境

允许的表单3:

int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */

评论:

C99删除了“隐式int”,因此main()不再有效。

引入了一个奇怪的、模棱两可的句子“或以某种其他实现定义的方式”。这可以被解释为“intmain()的参数可能不同”或“main可以有任何实现定义的形式”。

一些编译器选择以后一种方式解释标准。可以说,人们不能轻易地通过引用标准本身来说明他们不符合标准,因为它是模棱两可的。

然而,允许main()的完全野生形式可能不是(?)这个新句子的意图。C99的基本原理(非规范性)意味着该语句引用了int main 4的附加参数。

然而,托管环境程序终止部分继续讨论main不返回int 5的情况。尽管该部分对于如何声明main并不是规范性的,但它肯定意味着即使在托管系统上,main也可以以完全实现定义的方式声明。

C99独立环境

允许使用任何形式或名称的main。

C11托管环境

允许的表单7:

int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */

C11独立环境

允许使用任何形式或名称的main。


注意,在上述任何版本中,int main()从未被列为C的任何托管实现的有效形式。在C中,与C++不同,()和(void)有不同的含义。前者是一种过时的特征,可以从语言中删除。参见C11未来语言方向:

6.11.6函数声明器使用带有空括号的函数声明符(而不是原型格式参数类型声明符)是一种过时的特性。


C++03托管环境

允许的表单9:

int main ()
int main (int argc, char *argv[])

评论:

注意第一种形式中的空括号。在这种情况下,C++和C是不同的,因为在C++中,这意味着函数不带参数。但在C中,这意味着它可以采用任何参数。

C++03独立环境

启动时调用的函数的名称是实现定义的。如果它被命名为main(),它必须遵循所述的格式10:

// implementation-defined name, or 
int main ()
int main (int argc, char *argv[])

C++11托管环境

允许的表单11:

int main ()
int main (int argc, char *argv[])

评论:

标准文本已更改,但含义相同。

C++11独立环境

启动时调用的函数的名称是实现定义的。如果它被命名为main(),它必须遵循所述的形式12:

// implementation-defined name, or 
int main ()
int main (int argc, char *argv[])

工具书类

ANSI X3.159-1989 2.1.2.2托管环境。“程序启动”

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

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

或具有两个参数(这里称为argc和argv,但可以使用任何名称,因为它们是本地的声明它们的函数):

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

ANSI X3.159-1989 2.1.2.1独立环境:

在独立的环境中(C程序执行可能需要没有任何操作系统好处的地方)、名称和类型在程序启动时调用的函数的实现是定义的。

ISO 9899:1999 5.1.2.2托管环境->5.1.2.2.1程序启动

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

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

或具有两个参数(这里称为argc和argv,但可以使用任何名称,因为它们是本地的声明它们的函数):

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

或同等产品;9) 或在定义的某些其他实现中方式

国际标准编程语言-C的基本原理,修订版5.10。5.1.2.2托管环境-->5.1.2.2.1程序启动

main参数的行为,以及exit、main和atexit的交互作用(见§7.20.4.2)已被编入法典,以抑制argv表示中的一些不必要的变化字符串,以及main返回值的含义。

argc和argv作为main参数的规范承认了广泛的先前实践。argv[argc]必须是空指针,以便为列表末尾提供冗余检查,这也是基于常见的做法。

main是唯一一个可以用零或两个参数声明的函数。(其他函数的参数数量必须在调用和定义之间完全匹配。)这种特殊情况只是认识到当程序不访问程序参数字符串时,将参数保留为main的普遍做法。尽管许多实现支持两个以上的主要论点,但标准既不支持也不禁止这种做法;用三个参数定义main的程序并不严格符合(见§J.5.1.)。

ISO 9899:1999 5.1.2.2托管环境-->5.1.2.2.3程序终止

如果主函数的返回类型是与int兼容的类型,那么从初始调用到主函数的一个返回就相当于用主函数返回的值作为其参数调用退出函数;11) 到达终止主函数的}返回值0。如果返回类型与int不兼容,则返回到主机环境的终止状态未指定。

ISO 9899:1999 5.1.2.1独立环境

在一个独立的环境中(在这种环境中,C程序的执行可能没有任何操作系统的好处),程序启动时调用的函数的名称和类型是由实现定义的。

ISO 9899:2011 5.1.2.2托管环境->5.1.2.2.1程序启动

本节与上述C99节相同。

ISO 9899:1999 5.1.2.1独立环境

本节与上述C99节相同。

ISO 14882:2003 3.6.1主要功能

实现不应预先定义主功能。该功能不应过载。它应该具有int类型的返回类型,否则它的类型是实现定义的。所有实现应允许以下两种主要定义:

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

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

ISO 14882:2003 3.6.1主要功能

它定义了独立环境中的程序是否需要定义主功能。

ISO 14882:2011 3.6.1主要功能

实现不应预先定义主功能。该功能不应过载。它应该具有int类型的返回类型,否则它的类型是实现定义的。所有实施应允许两者-返回int和-返回int的(int,指向指向char的指针)函数作为主要类型(8.3.5)。

ISO 14882:2011 3.6.1主要功能

本节与上述C++03节相同。

main()的返回值显示程序如何退出。如果返回值为零,则表示执行成功,而任何非零值都表示执行中出现了问题。

main的返回值指示程序如何退出。正常出口由main的0返回值表示。异常退出是由非零返回发出的信号,但对于如何解释非零代码没有标准。正如其他人所指出的,void main()是C++标准所禁止的,不应该使用。有效的C++主签名是:

int main(void)

and

int main(int argc, char **argv)

相当于

int main(int argc, char *argv[])

还值得注意的是,在C++中,int main()可以不带return语句,此时默认返回0。C99项目也是如此。是否返回0;是否应被省略值得商榷。有效的C程序主签名的范围要大得多。

效率不是主要功能的问题。根据C++标准,只能输入和离开一次(标记程序的开始和终止)。对于C,允许重新输入main(),但应避免。

C89中的main()和K&R C未指定的返回类型默认为“int”。

return 1? return 0?

如果不在int main()中编写return语句,则默认情况下关闭的}将返回0。

(仅在c++和c99以后的版本中,对于c90,您必须编写return语句。请参阅为什么main不在此处返回0?)

父进程将接收返回0或返回1。在shell中,它会进入一个shell变量,如果您以shell形式运行程序而不使用该变量,则不必担心main()的返回值。

参见如何获取主函数返回的内容?。

$ ./a.out
$ echo $?

这样您可以看到它是变量$?它接收main()返回值的最低有效字节。

在Unix和DOS脚本中,成功时返回0,错误时返回非零。这是Unix和DOS脚本用来发现程序发生了什么并控制整个流程的标准。

这里有一个关于返回代码用法的小演示。。。

当使用Linux终端提供的各种工具时,可以使用返回代码,例如在过程完成后进行错误处理。假设存在以下文本文件myfile:

这是一些示例,以检查grep的工作原理。

执行grep命令时,将创建一个进程。一旦它通过(并且没有中断),它将返回一些介于0和255之间的代码。例如:

$ grep order myfile

如果你这样做

$ echo $?
$ 0

您将获得0。为什么?因为grep找到了匹配项并返回了退出代码0,这是成功退出的通常值。原因可能在于简单检查是否一切正常的布尔性质。对0(布尔值假)的简单否定返回1(布尔值真),这可以在if-else语句中轻松处理。

让我们再次检查它,但不在文本文件中,因此不会找到匹配项:

$ grep foo myfile
$ echo $?
$ 1

由于grep未能将标记“foo”与文件内容匹配,因此返回代码为1(这是发生故障时的常见情况,但如上所述,您有很多值可供选择)。同样,如果我们将其放在简单的布尔上下文中(一切正常与否),则否定1(布尔值为真)会产生0(布尔值假),这同样可以通过if-else语句轻松处理。当涉及布尔值时,任何不是0的值都被视为等同于1(因此,在用于检查是否发生错误的简单if-else语句中,2、3、4等的工作方式与使用1的情况相同)。您可以使用不同的返回值来增加错误状态的粒度。对于成功执行的状态,使用0以外的任何值都被认为是不好的做法(由于上面给出的原因)。

以下bash脚本(只需在Linux终端中键入它)虽然非常基本,但应该能给出一些错误处理的概念:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'; [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

在第二行之后,由于“foo”使grep返回1,所以没有任何内容打印到终端,我们检查grep的返回代码是否等于0。第二个条件语句在最后一行回显其消息,因为由于CHECK==1,它是真的。

正如您所看到的,如果您正在调用这个和那个进程,有时需要查看它返回了什么(通过main()的返回值),例如在运行测试时。