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

int main()

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

这相当于

int main(int argc, char** argv)

同样值得注意的是,在C++中,int main()可以没有返回语句,此时它默认返回0。C99程序也是如此。是否应该省略return 0;是有争议的。有效的C程序主签名的范围要大得多。

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

成功时返回0,错误时返回非零。这是UNIX和DOS脚本用来找出程序发生了什么的标准。

我相信main()应该返回EXIT_SUCCESSEXIT_FAILURE。它们在stdlib.h中定义

请记住,即使您返回的是int,一些操作系统(Windows)也会将返回的值截断为一个字节(0-255)。

公认的答案似乎是针对C++的,所以我想我应该添加一个与C有关的答案,这在几个方面有所不同。ISO/IEC 9899:1989(C90)和ISO/IEC 9899:1999(C99)之间也做了一些更改。

main()应该声明为:

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

或等效。例如,int main(int argc, char *argv[])等效于第二个。在C90中,int返回类型可以省略,因为它是默认值,但在C99及更高版本中,int返回类型不能省略。

如果实现允许,main()可以以其他方式声明(例如int main(int argc, char *argv[], char *envp[])),但这会使程序实现定义,不再严格符合。

标准定义了3个严格符合的返回值(即不依赖于实现定义的行为):0EXIT_SUCCESS表示成功终止,EXIT_FAILURE表示不成功终止。任何其他值都是非标准的,实现定义。在C90中,main()必须在末尾有一个显式的return语句,以避免未定义的行为。在C99及更高版本中,您可以省略main()的返回语句。如果您这样做,并且main()完成,则有一个隐式的return 0

最后,从标准的角度来看,从C程序调用main()递归没有错。

如果您确实存在与从进程返回整数的效率相关的问题,您可能应该避免多次调用该进程,以至于此返回值成为问题。

如果您正在这样做(多次调用进程),您应该找到一种方法将您的逻辑直接放在调用者内部或DLL文件中,而无需为每个调用分配特定的进程;在这种情况下,多个进程分配会给您带来相关的效率问题。

详细地说,如果你只想知道返回0是否比返回1更有效率,在某些情况下,它可能取决于编译器,但一般来说,假设它们是从同一个源(本地、字段、常量、嵌入在代码中、函数结果等)读取的,它需要完全相同数量的时钟周期。

返回什么取决于你想对可执行文件做什么。例如,如果你在命令行shell中使用程序,那么你需要返回0表示成功,返回非零表示失败。然后你就可以在shell中使用程序,并根据代码的结果进行条件处理。此外,你可以根据你的解释分配任何非零值,例如对于严重错误,不同的程序退出点可以终止具有不同退出值的程序,调用shell可以通过检查返回的值来决定该怎么做。如果代码不打算与shell一起使用,并且返回的值不会打扰任何人,那么它可能会被省略。我个人使用签名int main (void) { .. return 0; .. }

操作系统可以使用返回值来检查程序是如何关闭的。

在大多数操作系统中,返回值0通常表示OK(无论如何我能想到的操作系统)。

当您自己调用进程时,也可以检查它,看看程序是否正确退出和完成。

不是只是一个编程约定。

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

main()在C89和K&R C未指定返回类型默认为'int'。

return 1? return 0?
  1. 如果您没有在int main()中编写返回语句,关闭}将默认返回0。

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

  1. 父进程将接收return 0return 1。在shell中,它进入shell变量,如果您正在以shell形式运行程序而不使用该变量,那么您无需担心main()的返回值。

如何获取我的main函数返回的内容?

$ ./a.out$ echo $?

通过这种方式,您可以看到变量$?接收main()返回值的最低有效字节。

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

返回0应该告诉程序员程序已成功完成工作。

标准C-托管环境

对于托管环境(这是正常的环境),C11标准(ISO/IEC 9899:2011)规定:

5.1.2.21程序启动

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

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

或两个参数(这里称为argcargv,尽管任何名称都可能是使用,因为它们是声明它们的函数的本地):

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

或等效;10)或以其他实现定义的方式。

如果声明了它们,main函数的参数应遵循以下规则限制:

  • argc的值应该是非负的。
  • argv[argc]应该是一个空指针。
  • 如果argc的值大于零,则数组成员argv[0]argv[argc-1](包括0)应包含指向字符串的指针,这些指针是给定的程序启动前由主机环境定义的值目的是在程序启动之前向程序提供确定的信息来自托管环境中的其他地方。如果主机环境不能提供带有大写和小写字母的字符串,实现应确保字符串以小写形式接收。
  • 如果argc的值大于零,则argv[0]指向的字符串表示程序名称;argv[0][0]应为空字符,如果主机环境中没有可用的程序名。如果argc的值是大于1,由argv[1]argv[argc-1]指向的字符串表示程序参数。
  • 参数argcargv以及argv数组指向的字符串应可以被程序修改,并在程序之间保留它们最后存储的值启动和程序终止。

10)因此,int可以被定义为int的typedef名称替换,或者argv的类型可以写为char **argv,等等

C99或C11中的程序终止

main()返回的值以实现定义的方式传输到“环境”。

5.1.2.23方案终止

1如果main函数的返回类型是与int兼容的类型,则从对main函数的初始调用等价于使用值调用exit函数由main函数返回作为其参数;11)到达}终止main函数返回值0。如果返回类型与int不兼容,则返回到主机环境的终止状态未指定。

11)根据6.2.4,main中声明的具有自动存储持续时间的对象的生命周期会在前一种情况下结束,即使他们不会在后者。

请注意,0被授权为“成功”。如果您愿意,您可以从<stdlib.h>中使用EXIT_FAILUREEXIT_SUCCESS,但0是公认的,1也是如此。另见退出代码大于255-可能吗?

在C89中(因此在Microsoft C中),没有关于如果main()函数返回但没有指定返回值会发生什么的语句;因此导致未定义的行为。

7.22.4.4exit函数

最后,控制权返回主机环境。如果status的值为零或EXIT_SUCCESS,则返回状态成功终止的实现定义形式。如果status的值为EXIT_FAILURE,则返回状态不成功终止的实现定义形式。否则返回的状态是实现定义的。

标准C++-托管环境

C++11标准(ISO/IEC 14882:2011)规定:

3.6.1主要功能[basic.start.main]

¶1程序应包含一个名为main的全局函数,该函数是程序的指定开始。[…]

☆2实现不应预定义main函数。此函数不应重载。它应有一个int类型的返回类型,但其类型是实现定义的。所有实现应允许以下两种定义main:

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

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

在后一种形式中,argc应是从环境传递给程序的参数数在其中运行程序。如果argc非零,则应在argv[0]中提供这些参数通过argv[argc-1]作为指向以空结尾的多字节字符串(NTMBS)的初始字符的指针(17.5.2.1.4.2)和argv[0]应是指向表示用于调用程序的名称或""argc的值应为非负值。argv[argc]的值[注意:建议在argv之后添加任何其他(可选)参数。-结束注]

§3函数main不能在程序中使用。main的链接(3.5)是实现定义的。[…]

§5 main中的返回语句具有离开main函数的效果(自动销毁任何对象)存储持续时间)并以返回值作为参数调用std::exit。如果控制到达末尾的main而不会遇到返回语句,其效果是执行

return 0;

C++标准明确表示“它[main函数]应具有int类型的返回类型,但除此之外,它的类型是实现定义的”,并要求作为选项支持与C标准相同的两个签名。因此,C++标准直接不允许'val main()',尽管它无法阻止允许替代的非标准实现。请注意,C++禁止用户调用main(但C标准没有)。

C++11标准中§18.5启动和终止的段落与C11标准中§7.22.4.4#0函数的段落相同(如上所述),除了脚注(仅记录EXIT_SUCCESSEXIT_FAILURE<cstdlib>中定义)。

标准C-公共扩展

传统上,Unix系统支持第三种变体:

int main(int argc, char **argv, char **envp) { ... }

第三个参数是一个以null结尾的字符串指针列表,每个字符串都是一个环境变量,它有一个名称、一个等号和一个值(可能为空)。如果您不使用它,您仍然可以通过'extern char **environ;'获取环境。这个全局变量在POSIX中是唯一的,因为它没有声明它的标头。

这被C标准认为是一个常见的扩展,记录在附件J中:

###J.5.1环境参数

¶1在托管环境中,main函数接收第三个参数char *envp[],指向一个以空结尾的指向char的指针数组,每个指针都指向一个字符串提供有关程序执行环境的信息(5.1.2.2.1)。

microsoftc

微软VS 2010编译器很有趣。网站说:

main的声明语法是

 int main();

或者,可选地,

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

或者,可以将mainwmain函数声明为返回void(无返回值)。如果您将mainwmain声明为返回空,则不能使用返回语句将退出代码返回到父进程或操作系统。当mainwmain声明为void时,要返回退出代码,您必须使用exit函数。

我不清楚当一个void main()的程序退出时会发生什么(什么退出代码返回给父或操作系统)-MS网站也是沉默的。

有趣的是,MS没有规定C和C++标准要求的main()的双参数版本。它只规定了三参数形式,其中第三个参数是char **envp,一个指向环境变量列表的指针。

Microsoft页面还列出了一些其他替代方案-wmain(),它需要宽字符串,还有更多。

此页面的MicrosoftVisual Studio 2005版本没有列出void main()作为替代方案。MicrosoftVisual Studio 2008开始的版本会这样做。

标准C-独立式环境

如前所述,上述要求适用于托管环境。如果你使用的是独立环境(这是托管环境的替代方案),那么标准就没什么可说的了。对于独立环境,程序启动时调用的函数不需要调用main,它的返回类型也没有限制。标准说:

5.1.2执行环境

定义了两个执行环境:独立式和托管式。在这两种情况下,当执行调用指定的C函数时,程序启动发生环境。所有具有静态存储持续时间的对象都应在程序启动前初始化(设置为它们的初始值)。否则未指定此类初始化的方式和时间。程序终止将控制权返回给执行环境。

5.1.2.1独立环境

在独立式环境中(C程序的执行可以在没有操作系统任何好处的情况下进行),程序启动时调用的函数的名称和类型是实现定义的。独立式程序可用的任何库设施,除了第4条要求的最小集之外,都是实现定义的。

独立环境中程序终止的效果是实现定义的。

对第4条一致性的交叉引用是指:

严格遵守程序应仅使用本国际标准中规定的语言和库的那些特性。3)它不应产生依赖于任何未指定、未定义或实现定义的行为的输出,并且不得超过任何最低实现限制。

符合实现的两种形式是主办独立式。A符合托管实施应接受任何严格符合的程序。A符合独立实现应接受任何严格符合的程序,其中库子句(第7条)中指定的特性的使用仅限于标准标头<float.h><iso646.h><limits.h><stdalign.h><stdarg.h><stdbool.h><stddef.h><stdint.h>,和<stdnoreturn.h>。符合要求的实现可能具有扩展(包括额外的库函数),只要它们不改变任何严格符合程序的行为。4)

7 A符合程序是符合标准的实现可以接受的。5)

3)严格符合的程序可以使用条件特征(参见6.10.8.3),前提是使用相关宏的适当条件包含预处理指令来保护使用。例如:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined *//* ... */fesetround(FE_UPWARD);/* ... */#endif

4)这意味着,除了本标准中明确保留的标识符之外,符合标准的实现不保留任何标识符。

5)严格符合程序旨在在符合要求的实现之间最大限度地可移植。符合要求的程序可能取决于符合要求的实现的不可移植特征。

值得注意的是,实际定义任何函数的独立环境所需的唯一标头是<stdarg.h>(甚至那些可能是-并且通常是-只是宏)。

标准C++-独立式环境

正如C标准同时识别托管和独立环境一样,C++标准也是如此。(引用自ISO/IEC 14882:2011。)

1.4履约情况[intro.compliance]

☆7定义了两种实现:托管实现独立实现。对于托管实现,本国际标准定义了可用库的集合。一个独立的实现是一种可以在没有操作系统的情况下执行的实现,并且具有一组实现定义的库,其中包括某些语言支持库(17.6.1.3)。

☆8符合要求的实现可能具有扩展(包括附加的库函数),前提是它们不会改变任何格式良好的程序的行为。需要实现来诊断以下程序使用根据本标准格式不正确的扩展。但是,这样做后,他们可以编译和执行此类程序。

每个实现都应包含留档,以标识它不支持的所有条件支持的构造,并定义所有特定于语言环境的特征。3

3)此留档还定义了实现定义的行为;参见1.9。

17.6.1.3独立实现[合规性]

定义了两种实现:托管和独立式(1.4)。对于托管实现,本国际标准描述了一组可用的标头。

一个独立的实现有一个实现定义的头集。这个集合至少应该包括表16所示的头。

提供的标头版本<cstdlib>应至少声明函数abortatexitat_quick_exitexitquick_exit(18.5)。此表中列出的其他标头应满足与托管实现相同的要求。

表16-独立实现的C++标头

Subclause                           Header(s)
                                    <ciso646>18.2  Types                         <cstddef>18.3  Implementation properties     <cfloat> <limits> <climits>18.4  Integer types                 <cstdint>18.5  Start and termination         <cstdlib>18.6  Dynamic memory management     <new>18.7  Type identification           <typeinfo>18.8  Exception handling            <exception>18.9  Initializer lists             <initializer_list>18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>20.9  Type traits                   <type_traits>29    Atomics                       <atomic>

如何在C中使用int main()

C11标准的标准§5.1.2.2.1显示了首选符号——int main(void)——但标准中也有两个示例显示了int main()§6.5.3.4§8§6.7.6.320。现在,需要注意的是,示例不是“规范的”;它们只是说明性的。如果示例中有错误,它们不会直接影响标准的正文。也就是说,它们强烈地表明了预期的行为,所以如果标准在示例中包括int main(),它表明int main()不是禁止的,即使它不是首选符号。

6.5.3.4sizeof_Alignof操作符

¶8示例3在此示例中,计算变长数组的大小并从函数返回:

#include <stddef.h>
size_t fsize3(int n){char b[n+3]; // variable length arrayreturn sizeof b; // execution time sizeof}int main(){size_t size;size = fsize3(10); // fsize3 returns 13return 0;}

int main(){ … }这样的函数定义确实指定该函数不接受参数,但不提供函数原型AFAICT。对于main()来说,这很少是问题;但这确实意味着如果你对main()进行递归调用,参数将不会被检查。对于其他函数,这更是一个问题-在调用函数时,你真的需要一个范围内的原型来确保参数是正确的。

你通常不会递归调用main(),在IOCCC这样的地方之外-并且在C++中明确禁止这样做。我确实有一个测试程序可以做到这一点-主要是为了新颖。如果你有:

int i = 0;int main(){if (i++ < 10)main(i, i * i);return 0;}

并且使用GCC编译并且不包含-Wstrict-prototypes,它在严格警告下编译干净。如果它是main(void),它无法编译,因为函数定义说“没有参数”。

这里有一个返回码用法的小演示…

当使用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,它是true。

正如您可以看到的,如果您正在调用这个和那个进程,有时必须查看它返回了什么(通过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独立式环境

允许任何形式或名称的main2

  • C99托管环境

允许的形式3

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

评论:

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

引入了一个奇怪的、模棱两可的句子“或以其他实现定义的方式”。这可以解释为“int main()的参数可能会变化”或“main可以具有任何实现定义的形式”。

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

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

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

  • C99独立式环境

允许任何形式或名称的main6

  • C11托管环境

允许的形式7

int main (void)int main (int argc, char *argv[])/* or in some other implementation-defined manner. */
  • C11独立式环境

允许任何形式或名称的main8


请注意,在上述任何版本中,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, orint 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, orint main ()int main (int argc, char *argv[])

参考文献

  1. ANSI X3.159-19892.1.2.2托管环境。”程序启动”

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

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

或两个参数(这里称为Argc和Argv,尽管可以使用任何名称,因为它们是本地的声明它们的函数):

int main(int argc, char *argv[]) { /* ... */ }
  1. ANSI X3.159-19892.1.2.1独立式环境:

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

  1. ISO 9899:19995.1.2.2托管环境->5.1.2.2.1程序启动

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

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

或两个参数(这里称为Argc和Argv,尽管可以使用任何名称,因为它们是本地的声明它们的函数):

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

或等效;9)或在其他一些实现定义方式。

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

main的参数的行为,以及退出、main和at退出的交互(见§7.20.4.2)已被编纂,以遏制一些不必要的品种在表示的argv字符串,以及main返回的值的含义。

将Argc和Argv作为main的参数的规范认可了广泛的先前实践。

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

  1. ISO 9899:19995.1.2.2托管环境-->5.1.2.2.3程序终止

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

  1. ISO 9899:19995.1.2.1独立环境

在独立的环境中(其中C程序的执行可以在没有操作系统任何好处的情况下进行),程序启动时调用的函数的名称和类型是实现定义的。

  1. ISO 9899:20115.1.2.2托管环境->5.1.2.2.1程序启动

本节与上述C99节相同。

  1. ISO 9899:19995.1.2.1独立环境

本节与上述C99节相同。

  1. ISO 14882:2003 3.6.1主要功能

实现不得预先定义main函数。此函数不得重载。它应具有int类型的返回类型,但其类型是实现定义的。所有实现都应允许以下两种main定义:

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

int main(int argc, char* argv[]) { /* ... */ }
  1. ISO 14882:2003 3.6.1主要功能

独立环境中的程序是否需要定义主函数是由实现定义的。

  1. ISO 14882:2011 3.6.1主要功能

实现不应预定义主函数。此函数不应重载。它应具有int类型的返回类型,但否则其类型是实现定义的。所有实现都应允许

-函数()返回int和

-(int的函数,指向char的指针)返回int

作为main(8.3.5)的类型。

  1. ISO 14882:2011 3.6.1主要功能

本节与上面引用的C++03相同。

忽略return 0

当C或C++程序到达main的末尾时,编译器将自动生成返回0的代码,因此无需将return 0;显式放在main的末尾。

备注:当我提出这个建议时,它几乎总是跟着两种评论之一:“我不知道。”或“这是个糟糕的建议!”我的理由是,依赖标准明确支持的编译器行为是安全和有用的。对于C,从C99开始;参见ISO/IEC 9899:1999第5.1.2.2.3节:

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

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

如果控制到达main的末尾而没有遇到返回语句,则效果是执行返回0;

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

此外,C++核心准则包含多个在main末尾省略return 0;的实例,并且没有写入显式返回的实例。尽管该文档中还没有关于这个特定主题的特定指南,但这似乎至少是对这种做法的默许。

所以我提倡省略它;其他人不同意(通常是强烈的!)无论如何,如果你遇到省略它的代码,你就会知道它是由标准明确支持的,你就会知道它的意思。

在C和C++中定义main()函数的正确(最有效)方法是什么?

“(最有效的)”这些词不会改变问题。除非你在一个独立的环境中,否则有一种普遍正确的方式来声明main(),那就是返回int

main()在C和C++中应该返回什么?

一个int,纯粹而简单。它不仅仅是“main()应该返回什么”,它是“必须main()返回什么”。main()当然是其他人调用的函数。你无法控制调用main的代码。因此,你必须声明main并使用类型正确的签名来匹配它的调用者。在这个问题上你别无选择。你不必问自己效率更高或更低,或者风格更好或更差,或者类似的问题,因为答案已经由C和C+标准为你完美定义了。请遵循它们。

如果int main()则返回1或返回0?

0代表成功,非零代表失败。同样,不是你需要(或去)选择的东西:它是由你应该遵守的接口定义的。

在C中,C11标准第5.1.2.2.1节(强调我的):

它将被定义为返回类型#0并且没有参数:

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

或两个参数(这里称为argcargv,虽然可以使用任何名称,因为它们是它们所在函数的本地名称已发布):

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

然而,对于像我这样的初学者来说,一个抽象的例子可以让我掌握它:

当你在程序中编写方法时,例如int read_file(char filename[LEN]);,作为该方法的调用者,你想知道是否一切顺利(因为可能会发生故障,例如找不到文件)。通过检查方法的返回值,你可以知道是否一切顺利,这是该方法向你发出成功执行(或不成功)信号的机制,并让调用者(例如在你的主方法中)决定如何处理意外的失败。

所以现在想象一下,我为一个更复杂系统中使用的微机制编写一个C程序。当系统调用微机制时,它想知道一切是否按预期进行,这样它就可以处理任何潜在的错误。如果C程序的main方法将返回false,那么调用系统怎么知道其子系统(微机制)的执行?它不能,这就是为什么main()返回int,以便将成功(或不成功)执行传达给调用者。

换句话说:

原因是主机环境(即操作系统)需要知道程序是否正确完成。如果没有int兼容类型作为返回类型(例如无效),“返回到主机环境的状态是未指定的”(即大多数操作系统上的未定义行为)。

在Windows上,如果程序因访问冲突而崩溃,则退出代码将为STATUS_ACCESS_VIOLATION (0xC0000005)。对于来自x86异常的其他类型的崩溃也类似。

因此,除了您从main返回或传递给exit的内容之外,还有其他内容可以导致看到退出代码。

“int”现在被ISO强制用于C和C++作为“main”的返回类型。

这两种语言以前都允许隐式“int”,并且允许在没有任何返回类型的情况下声明“main”。事实上,C++本身的第一个外部版本(1985年2月的“cFront”版本E)是用自己的语言编写的,声明“main”没有任何返回类型…但返回一个整数值:错误数量或127,以较小者为准

关于返回什么的问题:C和C++的ISO标准与POSIX标准同步工作。对于任何符合POSIX标准的托管环境,
(1)126保留给OS的shell以指示不可执行的实用程序,
(2)127为OS的shell保留,以指示未找到的命令,
(3)实用程序的退出值在实用程序的基础上单独说明,
(4)在shell之外调用实用程序的程序应该为自己的退出使用类似的值,
(5)值128及以上用于表示接收信号导致的终止,
(6)值1-125表示失败,
(7)值0表示成功。

在C和C++值EXIT_SUCCESS和EXIT_FAILURE用于处理最常见的情况:报告成功或只是通用失败的程序。它们可以(但不需要)分别等于0和1。

这意味着,如果您希望程序为不同的故障模式或状态指示返回不同的值,同时继续使用这两个常量,您可能必须首先确保您的附加“故障”或“状态”值严格介于max(EXIT_SUCCESS,EXIT_FAILURE)和126之间(并希望两者之间有足够的空间),并保留EXIT_FAILURE来标记通用或默认故障模式。

否则,如果您不打算使用常量,那么您应该遵循POSIX的要求。

对于打算在独立环境或不符合POSIX的主机上使用的程序,我不能再说什么,除了以下内容:

我已经编写了独立的程序——作为自定义运行时系统上的多线程程序(以及其他一切的自定义工具库)。我遵循的一般规则是:
(1)“main”运行前台进程,通常只包括启动、配置或初始化例程,但也可以包括用于持续操作的前台进程(如轮询循环),
(2)“main”返回到无限睡眠和等待循环中,
(3)没有定义或使用“main”的返回值,
(4)后台进程单独运行,作为中断驱动和事件驱动的线程,独立于“main”,仅通过接收重置信号或其他线程终止……或者通过简单地关闭对驱动线程的任何事件的监控。