main()
函数的正确(最有效)方式是什么 - int main()
还是void main()
- 以及为什么?参数又是怎样的呢?
如果选择int main()
,那么是return 1
还是return 0
?main()
函数的正确(最有效)方式是什么 - int main()
还是void main()
- 以及为什么?参数又是怎样的呢?
如果选择int main()
,那么是return 1
还是return 0
?main
函数的返回值表示程序如何退出。从main
正常退出的情况下,返回值为0。非正常退出时,返回一个非零值,但没有标准规定如何解释非零返回值。正如其他人指出的那样,C++标准禁止使用void main()
,应该避免使用。有效的C++main
函数签名包括:
int main(void)
和
int main(int argc, char **argv)
等价于
int main(int argc, char *argv[])
值得注意的是,在C++中,int main()
可以没有返回语句,此时默认返回0。在C99程序中也是如此。是否省略return 0;
存在争议。有效的C程序main函数签名范围更广。
main
函数的效率不是问题。根据C++标准,它只能被输入和离开一次(标志着程序的开始和终止)。对于C语言,允许重新进入main()
,但应避免这样做。
看起来已经有一个针对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 个返回值是严格符合标准(即不依赖于实现定义行为)的值:0
和 EXIT_SUCCESS
表示成功终止,EXIT_FAILURE
表示失败终止。任何其他的返回值都是非标准和实现定义的。在 C90 中,main()
必须在结尾处有一个显式的 return
语句来避免未定义的行为。在 C99 和更新版本中,可以省略 main()
的返回语句。如果你这样做了,当 main()
结束时,会有一个隐含的 return 0
。
最后,从标准的角度来看,从 C 程序中递归调用 main()
是没有问题的。
对于托管环境(即正常情况下),C11标准(ISO/IEC 9899:2011)规定:
5.1.2.2.1 程序启动main()
函数返回的值以一种实现定义的方式传递给“环境”。
请注意,将5.1.2.2.3 程序终止
1 如果
main
函数的返回类型与int
兼容,那么从初始调用main
函数返回等同于使用main
函数的返回值作为参数调用exit
函数;到达终止main
函数的}
将返回值0。如果返回类型与int
不兼容,则返回给主机环境的终止状态是未指定的。注:根据6.2.4的规定,在前一种情况下,具有自动存储期限的对象在
main
中声明后其生命周期已结束,即使在后一种情况下也是如此。
0
指定为“成功”。如果您愿意,您可以使用<stdlib.h>
中的EXIT_FAILURE
和EXIT_SUCCESS
,但是0已经被广泛接受,1也是如此。另请参阅Exit codes greater than 255 — possible?。main()
函数返回但未指定返回值时会发生什么的说明;因此,这将导致未定义的行为。
7.22.4.4
exit
函数¶5 最后,控制权返回给主机环境。如果
status
的值为零或EXIT_SUCCESS
,则返回一个实现定义的成功终止状态形式。如果status
的值为EXIT_FAILURE
,则返回一个实现定义的失败终止状态形式。否则,返回的状态是实现定义的。
3.6.1 Main function [basic.start.main]
¶1 A program shall contain a global function called main, which is the designated start of the program. [...]
¶2 An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation defined. All implementations shall allow both of the following definitions of main:
int main() { /* ... */ }
and
int main(int argc, char* argv[]) { /* ... */ }
In the latter form
argc
shall be the number of arguments passed to the program from the environment in which the program is run. Ifargc
is nonzero these arguments shall be supplied inargv[0]
throughargv[argc-1]
as pointers to the initial characters of null-terminated multibyte strings (NTMBSs) (17.5.2.1.4.2) andargv[0]
shall be the pointer to the initial character of a NTMBS that represents the name used to invoke the program or""
. The value ofargc
shall be non-negative. The value ofargv[argc]
shall be 0. [Note: It is recommended that any further (optional) parameters be added afterargv
. —end note]¶3 The function
main
shall not be used within a program. The linkage (3.5) ofmain
is implementation-defined. [...]¶5 A return statement in main has the effect of leaving the main function (destroying any objects with automatic storage duration) and calling
std::exit
with the return value as the argument. If control reaches the end of main without encountering a return statement, the effect is that of executing
return 0;
C++标准明确表示:“主函数应具有类型为int
的返回类型,但其它部分的类型由实现定义”,并要求支持与C标准相同的两种签名作为选项。因此,《C++标准》直接不允许使用'void main()',尽管它无法阻止非标准实现允许使用其他替代方式。请注意,《C++标准》禁止用户调用main
(但C标准允许)。
在C++11标准的§18.5 启动和终止一段与C11标准的§7.22.4.4 exit
函数的一段完全相同(如上所引),除了一个脚注(仅记录了在<cstdlib>
中定义了EXIT_SUCCESS
和EXIT_FAILURE
)。
传统上,Unix系统支持第三种变体:
int main(int argc, char **argv, char **envp) { ... }
The declaration syntax for main is
int main();
or, optionally,
int main(int argc, char *argv[], char *envp[]);
Alternatively, the
main
andwmain
functions can be declared as returningvoid
(no return value). If you declaremain
orwmain
as returning void, you cannot return an exit code to the parent process or operating system by using a return statement. To return an exit code whenmain
orwmain
is declared asvoid
, you must use theexit
function.
对我来说,当一个带有void main()
的程序退出时(返回给父进程或操作系统的退出代码)是不清楚的,而微软网站也没有提供相关信息。
有趣的是,微软并没有规定C和C++标准要求的带有两个参数的main()
版本。它只规定了一个带有三个参数的形式,其中第三个参数是char **envp
,指向环境变量列表的指针。
微软页面还列出了一些其他替代方案-wmain()
可以使用宽字符字符串等。
Microsoft Visual Studio 2005 版本的 此页面 不列出 void main()
作为一种选择。从 Microsoft Visual Studio 2008 开始的版本则有。
如前所述,上述要求适用于托管环境。如果您在使用独立环境(这是托管环境的替代方案),那么标准将会较少涉及。对于独立环境,程序启动时调用的函数无需命名为 main
,并且其返回类型没有限制。标准如下所示:
值得注意的是,唯一需要在独立环境中定义任何函数的头文件是¶5 A strictly conforming program shall use only those features of the language and library specified in this International Standard.3) It shall not produce output dependent on any unspecified, undefined, or implementation-defined behavior, and shall not exceed any minimum implementation limit.
¶6 The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program. A conforming freestanding implementation shall accept any strictly conforming program in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers
<float.h>
,<iso646.h>
,<limits.h>
,<stdalign.h>
,<stdarg.h>
,<stdbool.h>
,<stddef.h>
,<stdint.h>
, and<stdnoreturn.h>
. A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.4)¶7 A conforming program is one that is acceptable to a conforming implementation.5)
3) A strictly conforming program can use conditional features (see 6.10.8.3) provided the use is guarded by an appropriate conditional inclusion preprocessing directive using the related macro. For example:
#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */ /* ... */ fesetround(FE_UPWARD); /* ... */ #endif
4) This implies that a conforming implementation reserves no identifiers other than those explicitly reserved in this International Standard.
5) Strictly conforming programs are intended to be maximally portable among conforming implementations. Conforming programs may depend upon non-portable features of a conforming implementation.
<stdarg.h>
(甚至这些也可能是 - 通常是 - 宏)。<cstdlib>
应至少声明函数abort
、atexit
、at_quick_exit
、exit
和quick_exit
(18.5)。此表中列出的其他头文件应满足与托管实施相同的要求。
表16 — 用于独立实现的C++头文件
子句 | 头文件 |
---|---|
<ciso646> |
|
18.2 类型 | <cstddef> |
18.3 实现属性 | <cfloat> <limits> <climits> |
18.4 整数类型 | <cstdint> |
18.5 启动和终止 | <cstdlib> |
18.6 动态内存管理 | <new> |
18.7 类型识别 | <typeinfo> |
18.8 异常处理 | <exception> |
18.9 初始化列表 | <initializer_list> |
18.10 其他运行时支持 | <cstdalign> <cstdarg> <cstdbool> |
20.9 类型特性 | <type_traits> |
29 原子操作 | <atomic> |
int main()
有什么问题?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 The
sizeof
and_Alignof
operators…
¶8 EXAMPLE 3 In this example, the size of a variable length array is computed and returned from a function:
#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; }
int main(){ … }
这样的函数定义确实指定了该函数不接受任何参数,但并没有提供函数原型,据我所知。对于main()
来说,这很少是个问题;但这意味着如果你对main()
进行递归调用,参数将不会被检查。对于其他函数来说,这就成为了一个更大的问题——在调用函数时,你确实需要一个作用域内的原型来确保参数正确。main()
,而且在C++中明确禁止这样做。我确实有一个测试程序可以这样做——主要是为了新奇。如果你有:int i = 0;
int main()
{
if (i++ < 10)
main(i, i * i);
return 0;
}
-Wstrict-prototypes
选项,代码将在严格警告下编译通过。如果函数定义为main(void)
,则无法编译,因为函数定义中指定了“无参数”。int main(){ … }
这样的函数定义确实指定了该函数不需要参数,但是未提供函数原型(Function prototype),据我所知。对于 main()
来说,这很少是一个问题;这意味着如果您对 main()
进行递归调用,则不会检查参数。对于其他函数来说,这更成为一个问题——当调用函数时,确实需要一个在作用域内的原型,以确保参数正确。 - Jonathan Lefflermain()
。我有一个测试程序可以做到这一点——主要是为了新奇性。如果你有int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }
并使用GCC编译而没有包含-Wstrict-prototypes
,它将在严格的警告下编译通过。如果是main(void)
,它将无法编译。 - Jonathan Lefflermain()
函数有返回值,但他从未在main()
之前加上int
。你知道为什么吗?似乎这里的每个人都说应该写成int main()
,但是C语言的创造者在他的ANSI C书中并没有这样写。 - Joeint
,则不必为函数包括返回类型。如果在没有先前声明的情况下使用函数,则假定其返回int
。但是,C90标准不是当前标准。当前标准是C18,取代了C11和C99。———_[...继续...]_ - Jonathan Leffler我认为 main()
函数应该返回 EXIT_SUCCESS
或 EXIT_FAILURE
。它们在 stdlib.h
中被定义。
EXIT_SUCCESS
和EXIT_FAILURE
之所以存在,是因为一些历史操作系统(比如VMS?)使用不同于0的数字来表示成功。但是现在在任何地方都是0。 - fuzexit(EXIT_SUCCESS)
,这总是做正确的事情。 - Adrian McCarthy请注意,C和C++标准定义了两种实现方式:独立式和宿主式。
允许的形式 1:
int main (void)
int main (int argc, char *argv[])
main (void)
main (int argc, char *argv[])
/*... etc, similar forms with implicit int */
注释:
前两种形式是明确允许的,其他形式是由于C90允许函数返回类型和函数参数使用“implicit int”。不允许使用其他格式。
允许使用任何形式或名称的main 2。
允许使用以下格式3:
int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */
注释:
C99删除了“implicit int”,因此main()
不再有效。
一个奇怪、模棱两可的句子“或以其他某种实现定义的方式”被引入。这可以被解释为“int main()
的参数可能会有所不同”,也可以被解释为“main可以具有任何实现定义的形式”。
一些编译器选择以后者的方式解释标准。可以说,通过引用标准本身来证明它们不符合规范并不容易,因为它是模糊的。
然而,允许完全狂野的main()
形式可能(?)不是这个新句子的意图。 C99的理由(非规范性的)暗示该句子指的是int main
的附加参数4。
但是,主机环境程序终止的部分则继续讨论main
不返回int的情况5。虽然该部分对于如何声明main不是规范性的,但它肯定意味着即使在主机系统上,main也可能以完全实现定义的方式声明。
允许任何形式或名称的main 6。
允许的形式7:
int main (void)
int main (int argc, char *argv[])
/* or in some other implementation-defined manner. */
允许任何形式或名称的主函数8。
请注意,int main()
从未被列为上述版本中任何托管实现的C的有效形式。在C中,与C++不同,()
和 (void)
具有不同的含义。前者是一个过时的特性,可能会从语言中删除。参见C11将来的语言方向:
6.11.6 函数声明符号
使用带空括号的函数声明符号(不是原型格式参数类型声明符)是一个过时的特性。
允许的形式9:
int main ()
int main (int argc, char *argv[])
注释:
注意第一种形式中括号为空的情况。在C++和C语言中这是不同的,因为在C++中这表示函数不需要参数,而在C语言中则表示它可以接受任何参数。
在启动时调用的函数名称是实现定义的。如果它被命名为main()
,则必须遵循规定的形式10:
// implementation-defined name, or
int main ()
int main (int argc, char *argv[])
允许的形式11:
int main ()
int main (int argc, char *argv[])
评论:
标准的文本已经更改,但意思相同。
在启动时调用的函数的名称是实现定义的。 如果它被命名为main()
,则必须遵循规定的形式12:
// implementation-defined name, or
int main ()
int main (int argc, char *argv[])
参考资料
在程序启动时调用的函数名为main。实现不声明此函数的原型。它应该被定义为返回类型为int且没有参数:
int main(void) { /* ... */ }
或者使用两个参数(这里称为 argc 和 argv,但可以使用任何名称,因为它们是在声明它们的函数中局部的):
int main(int argc, char *argv[]) { /* ... */ }
在自由环境中(在此环境下,C程序可以在没有操作系统的情况下执行),调用程序启动时的函数的名称和类型是实现定义的。
程序启动时调用的函数名为 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,但这种做法既没有得到认可,也没有被禁止。定义具有三个参数的main的程序不是严格符合要求的(参见§J.5.1.)。
如果main函数的返回类型与int兼容,则从初始调用main函数返回相当于使用main函数返回值作为其参数调用exit函数;11)到达终止main函数的
}
返回0的值。如果返回类型与int不兼容,则返回给主机环境的终止状态是未指定的。
在自由环境中(其中C程序执行可以在没有操作系统任何好处的情况下进行),在程序启动时调用的函数的名称和类型是实现定义的。
这一节与引用的C99版节完全相同。
此部分与引用的C99版节完全相同。
一个实现不得预定义main函数。这个函数不能被重载。它应该有一个类型为int的返回类型,但是它的类型是由实现定义的。所有的实现都应该允许以下两种main函数的定义:
int main() { /* ... */ }
并且
int main(int argc, char* argv[]) { /* ... */ }
在自由环境下,程序是否需要定义主函数是由实现定义的。
实现不能预定义主函数。此函数不得重载。它应该具有int类型的返回类型,但其类型除此之外是由实现定义的。 所有实现都应允许使用以下两种类型之一作为main(8.3.5)类型:
— 返回int的()函数和
— 返回int的(int、char指针的指针)函数。
本节与上面引用的C++03版本相同。
int my_startup_function()
或int my_startup_function(int argc, char *argv[])
,但它可以将char my_startup_function(long argc, int *argv[])
作为启动函数吗?我想不行,对吧?而且,这不是也有歧义吗? - Utkumain()
,因为那么它必须使用列出的签名之一。我想最常见的签名应该是 void my_startup_function()
,因为在自由站系统上从程序返回是没有意义的。 - Lundinfunc()
被认为是过时的,草案本身在其自己的示例中使用int main()
。 - Antti Haapala -- Слава Україні成功返回0,错误返回非零值。这是UNIX和DOS脚本使用的标准,以了解程序发生了什么。
main()
在C89和K&R C中未指定返回类型,默认为“int”。
return 1? return 0?
int main()
中没有编写返回语句,那么默认情况下,闭合的 }
会返回 0(仅适用于 C++ 和 C99 及其更新版本,在 C90 中必须编写返回语句。详见Why main does not return 0 here?)。return 0
或 return 1
将由父进程接收。在 shell 中,它将进入一个 shell 变量中,如果您正在从 shell 运行程序而不使用该变量,则无需担心 main()
的返回值。详见How can I get what my main function has returned?。
$ ./a.out
$ echo $?
通过这种方式,您可以看到变量$?
接收了main()
的返回值的最低有效字节。
在Unix和DOS脚本中,通常成功时返回0
,出错时返回非零值。这是Unix和DOS脚本使用的标准,用于查找程序发生了什么并控制整个流程。
env
(不带任何参数),它会打印出环境,并且"$?"不会在环境中显示。 - Jonathan Leffler请记住,尽管您返回的是int类型,但某些操作系统(如Windows)会将返回值截断为一个字节(0-255)。
返回值可被操作系统用来检查程序如何关闭。
在大多数操作系统中,返回值0通常表示正常(至少我所知道的)。
当您自己调用进程并查看程序是否已退出且已正确完成时,也可以检查它。
这不仅仅是一种编程惯例。
main()
函数的返回值表示程序的退出状态。如果返回值为0
,则表示执行成功;而任何非零值都表示执行过程中发生了错误。
main
只被调用一次(在C++中只能被调用一次:没有递归)。如果您不希望执行在main
中花费大量时间,则不要多次调用程序:使程序实现重复。 - Kaz#include
语句。 - pukmain(...)
函数里使用了return
语句,你的系统将进入不可预测状态,你的洗衣机可能会变得自我意识并试图杀死你。因此,在这种情况下我们使用void main()
。这是裸机嵌入式的行业标准做法。 - 3Dave