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


694

28
我仍然认为它也相当模糊。为我定义“最有效”。在什么意义上有效?占用更少的内存?在某种意义上说运行得更快?我可以看到有用的答案,但我仍然认为问题的措词很差。
Onorio Catenacci

7
简而言之,高效的背景在这里很明显,尤其是带有示例的示例(可能在那里阐明了“高效”的定义)。希望缓冲不佳不会陷入困境,并完全后悔这个问题。可以说,无论是void还是int,都将返回一个值,因此它对文件大小,执行的操作或分配的内存都没有影响。而且,在大多数操作系统中,人们倾向于在成功时返回0,而在其他情况下(成功或失败)则返回0,但是没有标准。最终,效率没有任何明显的差异。
Kit10 2012年

“正确(最有效)”没有任何意义。高效是一回事,正确是另一回事。main被调用一次(在C ++中只能被调用一次:无递归)。如果您不希望执行花费大量时间在main,则不要多次调用该程序:使程序实现重复。
卡兹(Kaz)

2
我发现有趣的是,据我所知,没有一个答案提供了一个完整的工作示例,其中包括以下#include陈述
puk

3
在没有操作系统的平台上,返回值毫无意义。你什么也没回来。如果您returnmain(...)嵌入式设备上碰壁,系统将进入不可预知的状态,洗衣机将变得自我意识并试图杀死您。因此,我们void main()在这种情况下使用。这是裸金属嵌入式的行业标准做法。
3Dave

Answers:


569

返回值用于main指示程序如何退出。正常退出由的0返回值表示main。非零返回信号指示异常退出,但是对于如何解释非零代码没有标准。正如其他人所指出的那样,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()允许重新输入,但应避免。


69
可以多次输入/退出主程序,但该程序可能不会赢得任何设计大奖;)
korona

13
C99还具有C ++的错误功能,即到达main()函数的末尾等效于返回0 -如果main()被定义为返回与int兼容的类型(5.1.2.2.3节)。
乔纳森·莱夫勒

62
重新输入main无效的C ++。在标准中明确指出,3.6.1.3规定“不得在程序内使用main”
workmad3

117
stdlib.h为此提供了EXIT_SUCCESS和EXIT_FAILURE
Clay

20
0和非零是正确的,但对于阅读您的代码的人来说完全没有意义。这个问题可以证明人们不知道什么是有效/无效代码。EXIT_SUCCESS / EXIT_FAILURE更加清晰。
JaredPar

169

可接受的答案似乎是针对C ++的,因此我想添加一个与C有关的答案,这在某些方面有所不同。

ISO / IEC 9899:1989(C90):

main() 应该声明为:

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

或同等学历。例如,int main(int argc, char *argv[])等效于第二个。此外,int返回类型可以省略,因为它是默认值。

如果实现允许,main()则可以用其他方式声明,但这使程序实现已定义,并且不再严格遵循。

该标准定义了用于返回严格遵循(即不依赖于实现定义的行为)3个值:0EXIT_SUCCESS一个成功的终止,以及EXIT_FAILURE对不成功的终止。任何其他值都是非标准的,并且定义为实现。main()最后必须有一个明确的return声明,以避免未定义的行为。

最后,从标准的角度来看,main()从程序进行调用并没有错。

ISO / IEC 9899:1999(C99):

对于C99,除以下内容外,其他均与上面相同:

  • int返回类型不能省略。
  • 您可以省略中的return语句main()。如果这样做并main()完成,则存在一个隐式return 0

1
@Lundin我认为您不需要引号就可以允许某人制作接受不符合标准的程序的编译器,或者拥有不符合标准格式的编译器。这是常识和常识
KABoissonneault 2015年

4
@KABoissonneault实现定义的行为是标准中的术语,与完全未记录的行为相反。如果您实现列出为实现定义的行为的某些内容,则仍要遵循该标准。在这种情况下,被引用的C89没有列出这种实现定义的行为,因此没有引用的必要,以证明他并不仅仅是想办法。
隆丁

1
@Lundin您看到的是错误的方式。我们所谈论的不是实现定义的行为,而是他们所选择的偏离标准的实现。这更像是一个孩子违抗父母的行为:您不需要父母的报价就可以告诉您孩子可以采取什么方式违背父母的话。您只知道孩子选择这样做的那一刻,他们就不再遵守父母的规矩
KABoissonneault

2
@KABoissonneault我在评论中引用的部分肯定是关于实现定义的行为的(与非标准编译器扩展相对。)因此,我所说的是实现定义的行为。如果您对其他事情独白,那么祝您好运。
隆丁

1
@Lundin我想引号中的措词令人困惑(他们说“但这使程序实现定义了”的部分),但是我很确定该人正在谈论非标准行为(如“如果实现允许”和“并且不再严格符合[标准]”),而不是实际的实现定义的行为。这个人肯定应该改写他们的回答,但是我仍然认为不需要在标准中引用这句话
KABoissonneault

117

标准C —托管环境

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

5.1.2.2.1程序启动

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

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

或具有两个参数(尽管可以使用任何名称,因为它们在声明它们的函数中是局部的,所以在这里称为argcargv,):

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

或同等学历; 10)或其他实现定义的方式。

如果声明了它们,则主函数的参数应遵守以下约束:

  • 的值argc应为非负数。
  • argv[argc] 应为空指针。
  • 如果值argc是大于零,阵列成员argv[0]通过 argv[argc-1]包容应包含指向字符串,其通过前程序启动主机环境给定实现所定义的值。目的是从宿主环境中的其他位置向程序提供在程序启动之前确定的信息。如果主机环境不能提供带有大写和小写字母的字符串,则实现应确保以小写形式接收字符串。
  • 如果的值argc大于零,则由指向的字符串argv[0] 表示程序名称;argv[0][0]如果程序名称在主机环境中不可用,则为空字符。如果值argc大于一,字符串由指向argv[1]通过argv[argc-1] 表示该程序的参数。
  • 数组所指向的参数argcargv字符串argv应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。

10)因此,int可以用定义为的typedef名称代替int,或者argv可以将其类型写为 char **argv,等等。

C99或C11中的程序终止

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

5.1.2.2.3程序终止

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

11)根据6.2.4,main 在前一种情况下,声明具有自动存储期限的对象的生存期将结束,即使后者没有。

请注意,这0被强制为“成功”。如果愿意,可以使用EXIT_FAILUREand EXIT_SUCCESSfrom <stdlib.h>,但是0已被确立,1也是如此。另请参见大于255的退出代码-可能吗?

在C89中(因此在Microsoft C中),没有关于该main()函数返回但未指定返回值的情况的声明。因此,它导致不确定的行为。

7.22.4.4 exit功能

¶5最后,控制权返回到主机环境。如果值为status0或EXIT_SUCCESS,则返回状态成功终止的实现定义形式。如果值statusEXIT_FAILURE,地位的实现定义的形式成功终止返回。否则,返回的状态是实现定义的。

标准C ++ —托管环境

C ++ 11标准(ISO / IEC 14882:2011)指出:

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

¶1程序应包含一个称为main的全局功能,这是程序的指定开始位置。[...]

¶2实现不得预定义主要功能。此功能不得重载。它的返回类型应该是int类型,否则它的类型是实现定义的。所有实现均应允许以下两个main定义:

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

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

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

¶3该功能main不得在程序内使用。的链接(3.5)由main实现定义。[...]

¶5main中的return语句的作用是保留main函数(销毁具有自动存储持续时间的所有对象)并std::exit以return值作为参数进行调用。如果控制在没有遇到return语句的情况下到达了main的末尾,则其结果是执行

return 0;

C ++标准明确表示“ [[主函数]应该具有type的返回类型int,但否则其类型是实现定义的”,并且需要与C标准相同的两个签名作为支持选项。因此,C ++标准直接不允许使用“ void main()”,尽管它无法停止允许替代方法的非标准实现。请注意,C ++禁止用户调用main(但C标准不禁止)。

有§18.5的段开始和终止在C ++ 11标准,其是与从§7.22.4.4段落exit功能在C11标准(上面引述的),除了一个脚注(其简单地文档EXIT_SUCCESSEXIT_FAILURE定义在<cstdlib>)。

标准C —通用扩展

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

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

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

这是C标准认可的通用扩展,记录在附件J中:

J.5.1环境参数

¶1在托管环境中,主函数接收第三个参数,char *envp[]该参数指向以NULL终止的指针数组char,每个指针均指向一个字符串,该字符串提供有关此程序执行环境的信息(5.1。 2.2.1)。

微软C

微软VS 2010的编译器是有趣的。该网站说:

main的声明语法是

 int main();

或者(可选)

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

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

我不清楚当程序void main()退出时会发生什么(退出代码返回给父代或OS),而MS网站也保持沉默。

有趣的是,MS并未规定main()C和C ++标准所需的两个参数的版本。它仅规定了三个参数形式,其中第三个参数为char **envp,它是指向环境变量列表的指针。

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

此页面的Microsoft Visual Studio 2005版本未列出。从Microsoft Visual Studio 2008开始的版本都可以。void main()

标准C-独立环境

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

5.1.2执行环境

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

5.1.2.1独立环境

在独立的环境中(其中C程序的执行可能没有操作系统的任何好处),在程序启动时调用的函数的名称和类型是实现定义的。除了第4节所要求的最小设置外,独立程序可用的任何库功能都是实现定义的。

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

交叉引用第4条“一致性”是指:

¶5 严格符合标准的程序应仅使用本国际标准中指定的语言和库的那些功能。3)它不应产生依赖于任何未指定,未定义或实现定义的行为的输出,并且不得超过任何最小实现限制。

¶6符合的实现的两种形式是托管的独立的。一个符合托管实施应当接受任何严格符合程序。甲符合独立执行应接受任何严格符合程序,其中使用的库条款(第7节)中指定的功能被限制在标准报头的内容<float.h><iso646.h><limits.h><stdalign.h><stdarg.h><stdbool.h><stddef.h><stdint.h>,和 <stdnoreturn.h>。符合条件的实现可以具有扩展(包括其他库函数),前提是它们不会改变任何严格符合程序的行为。4)

¶7 合格程序是合格实现可接受的程序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符合标准的实现可以扩展(包括其他库函数),前提是它们不会改变任何格式良好的程序的行为。需要实施以诊断使用了根据本国际标准格式错误的扩展程序的程序。但是,这样做后,他们可以编译和执行此类程序。

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

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

17.6.1.3独立实施[遵从性]

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

独立的实现具有实现定义的标头集。该集合至少应包括表16中所示的标头。

报头的提供的版本<cstdlib>应宣布至少功能abortatexitat_quick_exitexit,和quick_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>

int main()在C中使用呢?

C11标准的第§5.1.2.2.1标准显示了首选的表示法-  int main(void),但是该标准中还有两个示例显示int main()§6.5.3.4¶8§6.7.6.3¶20。现在,重要的是要注意示例不是“规范性的”。它们仅是说明性的。如果示例中有错误,则它们不会直接影响标准的正文。也就是说,它们强烈指示了预期的行为,因此,如果该标准包含int main()在示例中,则表明这int main()不是禁止的,即使它不是首选的表示法也是如此。

6.5.3.4 sizeof_Alignof运算符

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

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling:类似的函数定义int main(){ … }确实指定该函数不接受任何参数,但不提供函数原型AFAICT。因为main()那很少有问题。这意味着,如果您有对的递归调用main(),则不会检查参数。对于其他函数,这更多的是问题–调用该函数以确保参数正确时,您确实需要范围内的原型。
乔纳森·莱夫勒

1
@DavidBowling:通常main()在IOCCC之类的地方,您通常不会递归调用。我确实有一个执行该任务的测试程序-主要是为了新颖性。如果你有int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; } GCC并使用GCC进行编译,但不包含-Wstrict-prototypes,则在严格的警告下它会干净地编译。如果为main(void),则无法编译。
乔纳森·勒夫勒

61

我认为main()应该返回EXIT_SUCCESSEXIT_FAILURE。它们在stdlib.h


20
0也是标准的。
克里斯·杨

2
@ChrisYoung有EXIT_SUCCESSEXIT_FAILURE因为一些历史操作系统(VMS?)使用不同数量大于0表示成功。如今到处都是0。
fuz 2014年

5
@FUZxxl您是正确的,但这与我的评论没有冲突。EXIT_SUCCESS的确可以为非零,但是标准(C89,C99,C11)都将0(以及EXIT_SUCCESS)定义为状态成功终止的实现定义形式。
克里斯·杨

2
@FUZxxl:的确,VMS使用奇数(例如1)表示成功,使用偶数(例如0)表示失败。不幸的是,最初的ANSI C标准被解释为意味着EXIT_SUCCESS必须为0,因此从main返回EXIT_SUCCESS在VMS上的行为完全错误。VMS的可移植操作是使用exit(EXIT_SUCCESS),它总是做正确的事情。
阿德里安·麦卡锡

1
5.1.2.2.3“如果主函数的返回类型是与int兼容的类型,则从初始调用到主函数的返回等同于以主函数返回的值作为参数来调用退出函数; 11)到达终止主函数的},将返回值0。”
伦丁

38

请注意,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()不再有效。

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

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

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

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

  • C99独立式环境

    main的任何形式或名称都是允许的6

  • C11托管环境

    允许的表格7

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

    主的任何形式或名称都是允许的8


请注意,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[])

参考文献

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

    程序启动时调用的函数称为main。该实现没有为此函数声明任何原型。它应使用返回类型int且不带参数来定义:

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

    或具有两个参数(尽管可以使用任何名称,但在此处称为argc和argv,因为它们是声明它们的函数的局部名称):

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

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

  3. 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)或以其他一些实现定义的方式。

  4. 国际标准的理由-编程语言-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的两个以上论点,但该实践并没有受到标准的祝福或禁止;用三个参数定义main的程序不是严格符合的(请参见§J.5.1。)。

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

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

  6. ISO 9899:1999 5.1.2.1独立环境

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

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

    本部分与以上引用的C99相同。

  8. ISO 9899:1999 5.1.2.1独立环境

    本部分与以上引用的C99相同。

  9. ISO 14882:2003 3.6.1主要功能

    实现不得预定义主要功能。此功能不得重载。它的返回类型应该是int类型,否则它的类型是实现定义的。所有实现均应允许以下两个main定义:

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

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

    是否需要独立环境中的程序来定义主要功能由实现定义。

  11. ISO 14882:2011 3.6.1主要功能

    实现不得预定义主要功能。此功能不得重载。它的返回类型应该是int类型,否则它的类型是实现定义的。所有实现都应允许

    —()返回int和的函数

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

    作为主要类型(8.3.5)。

  12. ISO 14882:2011 3.6.1主要功能

    本部分与以上引用的C ++ 03相同。


一个问题:C ++标准是否意味着在独立环境中启动功能的签名也已定义为实现?例如,一个实现可能已将启动函数定义为:,int my_startup_function ()或者int my_startup_function (int argc, char *argv[])也可以例如char my_startup_function (long argc, int *argv[])将其定义为启动函数?我猜不,对吧?另外,这也不是模棱两可的吗?
Utku

@Utku它可以具有任何签名,只要它没有命名就可以,main()因为它必须使用列出的签名之一。我想绝大多数是最常见的void my_startup_function (),因为从独立系统上的程序返回是没有意义的。
伦丁2015年

1
我知道了。但是,如果允许为启动功能使用任何名称和签名,那么为什么不也允许使用其他签名main呢?抱歉,如果这不是一个明智的问题,但我无法理解其背后的原因。
Utku

@Utku C和C ++在那里不同。至于为什么C ++强制执行此操作,我不知道,没有理由。我怀疑主要的罪魁祸首(双关语意为)是Stroustrup,他很早就宣布主要必须返回int,周期。因为当他制作第一个C ++版本时,他只被用于托管系统。在链接的文章中,Stroustrup 似乎仍然对独立实现的存在遗忘:例如,他无视于C标准的托管实现子章,而忽略了5.1.2.1章的存在。
伦丁2015年

1
关于C11标准草案,值得注意的是,尽管func()该草案已过时,但草案本身仍在int main()其示例中使用。
Antti Haapala

29

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


8

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

return 1? return 0?
  1. 如果您未在中编写return语句int main(),则{默认情况下关闭将返回0。

  2. return 0return 1将由父进程接收。在外壳程序中,它进入一个外壳程序变量,如果您在外壳程序中运行程序而未使用该变量,则不必担心的返回值main()

请参阅如何获取主要函数返回的内容?

$ ./a.out
$ echo $?

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

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


4
严格来说,$?不是环境变量;它是外壳程序预定义(或内置)变量。区别很难发现,但是如果您运行env(不带任何参数),它将打印环境,并且$?不会在环境中显示。
乔纳森·莱夫勒

1
仅在C ++和C99及更高版本中,而在主要的“末日降临”中自动返回0。
卡兹(Kaz)

错别字:“关闭{”应为}。所以不允许我做这么小的编辑。
斯宾塞

7

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


4
Unix可以做到,其他大多数操作系统也一样。我知道VMS会产生如此令人难以置信的怪异事情,以至于返回EXIT_SUCCESS或EXIT_FAILURE之外的任何东西都会带来麻烦。
Leon Timmermans

2
MSDN可能会有所不同:通过mscorlib报告时,退出代码是一个带符号的32位整数。这似乎暗示截断出口代码的C运行时库有缺陷。

是的,这是不正确的。在Windows上,将返回32位整数(并将其转换为unsigned)。在具有32位整数的UNIX系统上,这是相同的。但是,在任何一个系统上,UNIX风格的外壳通常只会保留一个无符号的8位整数。
John McFarlane

4

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

在大多数操作系统中(无论如何我都会想到),返回值0通常意味着可以。

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

这是不是只是一个编程约定。


问题中没有任何内容表明存在操作系统。在独立的系统中,返回值没有任何意义。
伦丁

3

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


1
这是评论,而不是问题的答案。
伦丁

2

我的印象是,标准指定main不需要返回值,因为成功的返回是基于OS的(一个为零可能是另一个成功或失败),因此,不存在return是提示。编译器插入成功返回本身。

但是我通常返回0。


C99(和C ++ 98)允许您省略main的return语句;C89不允许您省略return语句。
乔纳森·莱夫勒

这是评论而不是答案。
伦丁

这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方发表评论。
史蒂夫·利利斯

6
@SteveLillis:在2008年,SO没有评论部分。
graham.reeds 2015年

2

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


main()正常状态返回1 表示发生了错误;返回0表示成功。如果您的程序总是失败,那么1是可以的,但这不是最好的主意。
Jonathan Leffler 2013年

1
@JonathanLeffler:返回的意思1,从main为实现定义。唯一由语言定义的值是0EXIT_SUCCESS(通常定义为0)和EXIT_FAILURE。在OpenVMS中,return 1;表示成功终止。
基思·汤普森

VMS不是“正常”的,这正是我所说的含义。是不是像“任何奇特的价值就是成功;在VMS上连值都失败了吗?
乔纳森·勒夫勒

2

忽略 return 0

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

注意:当我提出此建议时,几乎总是会出现以下两种评论之一:“我不知道。” 或“那是个坏建议!” 我的理由是,依靠该标准明确支持的编译器行为既安全又有用。对于C,自C99起;参见ISO / IEC 9899:1999第5.1.2.2.3节:

[...]从初始调用返回到main函数等同于以exit函数返回的值main作为参数来调用函数;到达}终止main函数的值将返回0。

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

如果控制在没有遇到return语句的情况下到达了main的末尾,则其结果是执行return 0的结果;

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

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

所以我主张省略它;其他人则不同意(通常是非常激烈!)无论如何,如果遇到忽略该代码的代码,您将知道该标准明确支持该标准,并且您将了解其含义。


2
这是一个不好的建议,因为仅实现C89而未实现任何更高标准的编译器仍然非常普遍(我在2017年编写),并且在可预见的将来仍将非常普遍。例如,上次我没有检查过任何版本的Microsoft编译器都实现了C99,并且据我了解,对于非GCC的嵌入式系统编译器,这仍然很常见。
zwol

4
@zwol:除了选择使用已经过期28年的过时的编译器之外,别无选择的人可能比决定是否明确包括要面临更多的问题return 0;,但是我要指出的是,那个时代的许多编译器return 0;甚至在隐式编译之前就实现了隐式编译。标准化。
爱德华

2
实际上,我做了很多嵌入式系统工作,并且return 0十多年来没有遇到过不支持隐式的编译器。Microsoft C的当前版本也支持它。也许您的信息已过期?
爱德华

2
我很欣赏这在C语言中引起争议,几乎(每个@zwol)。在C ++中,有关此问题的任何争议都是无稽之谈。
Lightness Races in Orbit

2
@爱德华我不是说争议不存在,我说这是胡说八道:P
Lightness Races in Orbit

1

返回什么取决于您要对可执行文件执行的操作。例如,如果将程序与命令行外壳一起使用,则成功返回0,失败返回非零。然后,您将能够在shell中使用程序进行条件处理,具体取决于代码的结果。您还可以根据您的解释指定任何非零值,例如,对于严重错误,不同的程序出口点可能会终止具有不同出口值的程序,并且调用外壳程序可以使用该值,通过检查返回的值来决定要做什么。如果该代码不适合与Shell一起使用,并且返回的值不会困扰任何人,则可以将其省略。我个人使用签名int main (void) { .. return 0; .. }


main()的格式由实现(即编译器)确定。除非编译器支持多种形式,否则程序员不会选择哪种形式。
伦丁

@Lundin返回类型将由实现来实现。但是要返回的值由程序员决定。C99第5.1.2.2.3节提到的返回类型与main兼容int。因此返回int将不是问题。尽管允许其他返回类型,但是在那种情况下,具有返回值的环境变量将是未指定的。但是,如果程序员这样做return 0;,则可以使用bash进行分支。
phoxis

1

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

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

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


1

这是返回码用法的一个小例子...

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

这是为了检查grep如何工作的一些示例。

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

$ grep order myfile

如果你这样做

$ echo $?
$ 0

您将得到0。为什么?因为grep找到一个匹配项并返回了退出代码0,这是成功退出的通常值。让我们再次检查一下,但文本文件中未包含某些内容,因此找不到匹配项:

$ grep foo myfile
$ echo $?
$ 1

由于grep无法将令牌“ foo”与我们文件的内容匹配,因此返回码为1(这是发生失败的常见情况,但如上所述,您可以选择很多值)。

现在,下面的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,第二条条件语句为true,因此第二条条件语句在最后一行回显其消息。 == 1。

如您所见,如果您正在调用此过程,则有时必须查看返回的内容(通过main()的返回值)。


在Shell脚本中,您将使用if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi-直接测试返回状态。如果要捕获状态(用于报告等),则可以使用分配。您可以使用if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fi或者可以使用三行。您还可以使用选项-s-qgrep以防止出现匹配项或例行错误消息。但是,这是shell细节-退出状态可能有用的关键-可以。
乔纳森·莱夫勒

1

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

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

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

没什么 应该 main()回报,这是什么 main()的回报。 main()当然是别人调用的功能。您对调用的代码没有任何控制权main()。因此,必须main()使用正确类型的签名进行声明以匹配其调用者。您根本没有选择的余地。您不必问自己什么效率更高或更低,哪种风格更好或更差,或者诸如此类,因为对于您而言,答案已经完全由C和C +标准定义。只要跟随他们。

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

0表示成功,非零表示失败。同样,您不需要(或无需选择)某些内容:它由您应该遵循的接口定义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.