提问人:Joel 提问时间:10/15/2008 最后编辑:DharmanJoel 更新时间:9/26/2023 访问量:453364
main() 在 C 和 C++ 中应该返回什么?
What should main() return in C and C++?
问:
在 C 和 C++ 中定义函数的正确(最有效)方法是什么——或者——为什么?那么论点呢?
如果那么或?main()
int main()
void main()
int main()
return 1
return 0
答:
的返回值指示程序的退出方式。正常退出由 0 返回值表示。异常退出由非零返回发出信号,但对于如何解释非零代码没有标准。正如其他人所指出的,被 C++ 标准禁止使用,不应使用。有效的 C++ 签名包括:main
main
void main()
main
int main(void)
和
int main(int argc, char **argv)
这相当于
int main(int argc, char *argv[])
还值得注意的是,在 C++ 中,可以不保留 return-statement,此时它默认返回 0。C99 程序也是如此。是否应该省略还有待商榷。有效的 C 程序主签名的范围要大得多。int main()
return 0;
效率不是功能的问题。根据 C++ 标准,它只能输入和离开一次(标记程序的开始和终止)。对于 C,允许重新输入,但应避免。main
main()
评论
成功时返回 0,错误时返回非零。这是 UNIX 和 DOS 脚本用来找出程序发生的情况的标准。
我相信应该返回或.它们在main()
EXIT_SUCCESS
EXIT_FAILURE
stdlib.h
评论
EXIT_SUCCESS
EXIT_FAILURE
exit(EXIT_SUCCESS)
请记住,即使您返回 int,某些操作系统 (Windows) 也会将返回的值截断为单个字节 (0-255)。
评论
unsigned
公认的答案似乎是针对 C++ 的,所以我想我会添加一个与 C 相关的答案,这在几个方面有所不同。ISO/IEC 9899:1989 (C90) 和 ISO/IEC 9899:1999 (C99) 之间也有一些变化。
main()
应声明为:
int main(void)
int main(int argc, char **argv)
或同等学历。例如,等同于第二个。在 C90 中,可以省略返回类型,因为它是默认值,但在 C99 及更高版本中,不能省略返回类型。int main(int argc, char *argv[])
int
int
如果实现允许,可以用其他方式声明(例如,),但这会使程序实现被定义,并且不再严格遵守。main()
int main(int argc, char *argv[], char *envp[])
该标准定义了 3 个严格符合(即不依赖于实现定义的行为)的返回值:成功终止和不成功终止。任何其他值都是非标准的,并且是已定义的实现。在 C90 中,必须在末尾有一个明确的语句,以避免未定义的行为。在 C99 及更高版本中,可以省略 return 语句。如果你这样做了,并且完成了,就会有一个隐含的.0
EXIT_SUCCESS
EXIT_FAILURE
main()
return
main()
main()
return 0
最后,从标准的角度来看,从 C 程序递归调用并没有错。main()
评论
如果您确实遇到与从进程返回整数的效率相关的问题,您可能应该避免多次调用该进程,以至于此返回值成为问题。
如果你正在这样做(调用一个进程很多次),你应该找到一种方法,将你的逻辑直接放在调用方或DLL文件中,而不为每个调用分配一个特定的进程;在这种情况下,多个进程分配会给您带来相关的效率问题。
详细来说,如果你只想知道返回 0 是否比返回 1 更有效率,在某些情况下它可能取决于编译器,但一般来说,假设它们是从同一源(本地、字段、常量、嵌入代码、函数结果等)读取的,它需要完全相同的时钟周期数。
返回的内容取决于要对可执行文件执行的操作。例如,如果将程序与命令行 shell 一起使用,则需要返回 0 表示成功,返回非零表示失败。然后,您将能够在 shell 中使用该程序,并根据代码的结果进行条件处理。此外,您可以根据自己的解释分配任何非零值,例如,对于严重错误,不同的程序出口点可以终止具有不同退出值的程序,并且调用 shell 可以使用该值,该 shell 可以通过检查返回的值来决定要做什么。
如果代码不打算与 shell 一起使用,并且返回的值不会打扰任何人,那么它可能会被省略。我个人使用签名int main (void) { .. return 0; .. }
评论
main
int
int
return 0;
操作系统可以使用返回值来检查程序的关闭方式。
返回值 0 通常意味着在大多数操作系统中正常(无论如何我都能想到的操作系统)。
当您自己调用进程时,也可以检查它,并查看程序是否退出并正确完成。
这不仅仅是一个编程约定。
评论
返回值 显示程序的退出方式。如果返回值为 ,则表示执行成功,而任何非零值都表示执行中出现问题。main()
zero
main()
在 C89 和 K&R C 中,未指定的返回类型默认为 'int'。
return 1? return 0?
- 如果不在 中编写 return 语句,则关闭将默认返回 0。
int main()
}
(仅在 c++ 和 c99 及更高版本中,对于 c90,您必须编写 return 语句。请在此处查看为什么 main 不返回 0?)
return 0
或将由父进程接收。在 shell 中,它进入 shell 变量,如果您从 shell 运行程序并且不使用该变量,则无需担心 的返回值。return 1
main()
请参阅如何获取我的主函数返回的内容?。
$ ./a.out
$ echo $?
这样你就可以看到,它是接收返回值 的最低有效字节的变量。$?
main()
在 Unix 和 DOS 脚本中,通常返回 on success 和 non-zero 表示错误。这是 Unix 和 DOS 脚本使用的标准,用于找出程序发生了什么并控制整个流程。return 0
评论
$?
env
$?
返回 0 应该告诉程序员程序已成功完成作业。
评论
main()
1
main
0
EXIT_SUCCESS
0
EXIT_FAILURE
return 1;
标准 C — 托管环境
对于托管环境(这是正常环境),C11 标准 (ISO/IEC 9899:2011) 说:
5.1.2.2.1 程序启动
程序启动时调用的函数名为 。实现声明否 此函数的原型。它的定义应为返回类型 和 不 参数:
main
int
int main(void) { /* ... */ }
或具有两个参数(此处称为 和 ,尽管任何名称都可以是 使用,因为它们是声明它们的函数的本地函数):
argc
argv
int main(int argc, char *argv[]) { /* ... */ }
或同等学历;10) 或以其他实现定义的方式。
如果声明了它们,则 main 函数的参数应遵循以下内容 约束:
- 的值应为非负数。
argc
argv[argc]
应为 null 指针。- 如果 的值大于零,则数组成员通过 inclusive 应包含指向字符串的指针,这些字符串被给出 在程序启动之前由主机环境实现定义的值。这 目的是向程序提供在程序启动之前确定的信息 从托管环境中的其他位置。如果主机环境无法 提供带有大写和小写字母的字符串,实现 应确保以小写形式接收字符串。
argc
argv[0]
argv[argc-1]
- 如果 的值大于零,则指向的字符串表示程序名称; 如果 程序名称在主机环境中不可用。如果 的值为 大于 1,则 through 指向的字符串表示程序参数。
argc
argv[0]
argv[0][0]
argc
argv[1]
argv[argc-1]
- 数组指向的参数和字符串应 可由程序修改,并在程序之间保留其最后存储的值 启动和程序终止。
argc
argv
argv
10)因此,可以用定义为 的 typedef 名称替换,或者 的类型可以写成 ,依此类推。
int
int
argv
char **argv
程序在 C99 或 C11 中终止
返回的值以实现定义的方式传输到“环境”。main()
5.1.2.2.3 程序终止
1 如果函数的返回类型是兼容的类型,则从 对函数的初始调用等同于使用以下值调用函数 由函数作为其参数返回;11) 到达终止函数将返回值 0。如果返回类型与 不兼容, 返回到主机环境的终止状态未指定。
main
int
main
exit
main
}
main
int
11) 根据 6.2.4,在前一种情况下,具有自动存储期限的对象的生命周期将结束,即使在后一种情况下不会结束。
main
请注意,这是“成功”的授权。如果您愿意,可以使用 and,但 0 是公认的,1 也是如此。另请参阅大于 255 的退出代码 — 可能?。0
EXIT_FAILURE
EXIT_SUCCESS
<stdlib.h>
在 C89(因此在 Microsoft C 中),没有关于函数返回但未指定返回值会发生什么的语句;因此,它会导致未定义的行为。main()
7.22.4.4 函数
exit
¶5 最后,将控制权交还给主机环境。如果 的值为零或 ,则返回状态成功终止的实现定义形式。如果值为 ,则返回状态 unsuccessful terminateation 的实现定义形式。否则,返回的状态为实现定义的状态。
status
EXIT_SUCCESS
status
EXIT_FAILURE
标准 C++ — 托管环境
C++11标准(ISO/IEC 14882:2011)说:
3.6.1 Main 函数 [basic.start.main]
¶1 程序应包含一个名为 main 的全局函数,该函数是程序的指定启动。[...]
¶2 实现不应预定义 main 函数。此功能不得过载。它应该 返回类型为 int 类型,但其类型为 implementation defined。 所有实现 应允许以下两个 main 定义:
int main() { /* ... */ }
和
int main(int argc, char* argv[]) { /* ... */ }
后一种形式应是从环境传递到程序的参数数 运行程序的地方。如果为非零,则这些参数应作为指向以 null 结尾的多字节字符串 (NTMBS) 的初始字符的指针提供 (17.5.2.1.4.2),并且应是指向表示 用于调用程序的名称或 。的值应为非负数。的值应为 0。[注意:建议在 之后添加任何其他(可选)参数。 —end 注意]
argc
argc
argv[0]
argv[argc-1]
argv[0]
""
argc
argv[argc]
argv
¶3 该函数不得在程序中使用。的链接 (3.5) 是实现定义的。[...]
main
main
¶5 main 中的 return 语句具有离开 main 函数的效果(使用 automatic 销毁任何对象 storage duration) 并使用返回值作为参数进行调用。如果控制到达终点 的 main 而不遇到 return 语句,效果是执行
std::exit
return 0;
C++ 标准明确规定“它 [main 函数] 应具有 type 的返回类型,但除此之外,它的类型是实现定义的”,并且需要与 C 标准相同的两个签名作为选项得到支持。因此,C++ 标准直接不允许使用“void main()”,尽管它无法阻止允许替代方案的非标准实现。请注意,C++ 禁止用户调用(但 C 标准没有)。int
main
C++11 标准中有一段 §18.5 开始和终止,与 C11 标准(如上引用)中的 §7.22.4.4 退出
函数中的段落相同,除了脚注(仅记录并在 中定义)。EXIT_SUCCESS
EXIT_FAILURE
<cstdlib>
标准 C — 通用扩展
传统上,Unix 系统支持第三种变体:
int main(int argc, char **argv, char **envp) { ... }
第三个参数是指向字符串的以 null 结尾的指针列表,每个字符串都是一个环境变量,该变量具有名称、等号和值(可能为空)。如果你不使用它,你仍然可以通过''访问环境。这个全局变量在 POSIX 中的变量中是独一无二的,因为它没有声明它的标头。extern char **environ;
这被 C 标准认可为通用扩展,记录在附录 J 中:
###J.5.1 环境参数
¶1 在托管环境中,main 函数接收第三个参数 , 指向以 null 结尾的指针数组,每个指针都指向一个字符串 它提供了有关此程序执行环境的信息 (5.1.2.2.1)。
char *envp[]
char
Microsoft C
Microsoft VS 2010编译器很有趣。该网站说:
main 的声明语法是
int main();
或者,可选地,
int main(int argc, char *argv[], char *envp[]);
或者,可以将 and 函数声明为返回(无返回值)。如果声明 或 返回 void,则不能使用 return 语句将退出代码返回到父进程或操作系统。若要在 或 声明为 时返回退出代码,必须使用该函数。
main
wmain
void
main
wmain
main
wmain
void
exit
我不清楚当一个程序退出时会发生什么(什么退出代码返回给父级或操作系统)——而且 MS 网站也是沉默的。void main()
有趣的是,MS 没有规定 C 和 C++ 标准所需的双参数版本。它只规定了一个三参数形式,其中第三个参数是 ,指向环境变量列表的指针。main()
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>
¶7 符合要求的程序是符合要求的实现可以接受的程序。五)
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 中所示的标头。
提供的标头版本应至少声明函数 、 、 和 (18.5)。此表中列出的其他标头应满足与托管实现相同的要求。
<cstdlib>
abort
atexit
at_quick_exit
exit
quick_exit
表 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> |
在 C 语言中使用呢?int main()
C11 标准的标准 §5.1.2.2.1 显示了首选符号 — — 但标准中也有两个示例显示:§6.5.3.4 ¶8 和 §6.7.6.3 ¶20。现在,重要的是要注意,例子不是“规范的”;它们只是说明性的。如果示例中存在错误,它们不会直接影响标准的正文。也就是说,它们强烈地表明了预期行为,因此,如果标准包含在示例中,则表明即使它不是首选符号,也不会被禁止。int main(void)
int main()
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; }
函数定义 like 指定函数不带参数,但不提供函数原型 AFAICT。因为这很少是问题;但这确实意味着,如果您有递归调用 ,则不会检查参数。对于其他函数,这更像是一个问题——当调用函数时,你确实需要一个范围内的原型,以确保参数是正确的。int main(){ … }
main()
main()
在像 IOCCC 这样的地方之外,你通常不会递归调用——而且在 C++ 中明确禁止你这样做。我确实有一个测试程序可以做到这一点——主要是为了新奇。如果您有:main()
int i = 0;
int main()
{
if (i++ < 10)
main(i, i * i);
return 0;
}
并使用 GCC 编译并且不包括 ,它会在严格的警告下干净地编译。如果是 ,则编译失败,因为函数定义说“没有参数”。-Wstrict-prototypes
main(void)
评论
int main(){ … }
main()
main()
main()
int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }
-Wstrict-prototypes
main(void)
main( )
main( )
int
int main( )
int
int
下面是一个返回代码用法的小演示......
使用 Linux 终端提供的各种工具时,可以使用返回代码,例如在过程完成后进行错误处理。假设存在以下文本文件 myfile:
这是一些示例,以检查 grep 的工作原理。
执行 grep 命令时,将创建一个进程。一旦它通过(并且没有中断),它就会返回 0 到 255 之间的一些代码。例如:
$ grep order myfile
如果你这样做
$ echo $?
$ 0
你会得到一个 0。为什么?因为 grep 找到了匹配项并返回了退出代码 0,这是成功退出的常用值。为什么会这样,可能在于简单检查是否一切正常的布尔性质。对 0 的简单否定(布尔值为 false)返回 1(布尔值为 true),这可以在 if-else 语句中轻松处理。
让我们再检查一次,但有些东西不在我们的文本文件中,因此找不到匹配项:
$ grep foo myfile
$ echo $?
$ 1
由于 grep 未能将标记“foo”与我们文件的内容匹配,因此返回码为 1(这是发生故障时的常见情况,但如上所述,您有很多值可供选择)。同样,如果我们把它放在简单的布尔上下文中(一切都好与否),否定 1(布尔值 true)会产生一个 0(布尔值 false),这同样可以通过 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。第二个条件语句在最后一行中回显其消息,因为它是 true,因为 CHECK == 1。
正如你所看到的,如果你正在调用这个和那个进程,有时必须查看它返回了什么(通过main()的返回值),例如在运行测试时。
评论
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
-q
grep
请注意,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 删除了“implicit int”,因此不再有效。main()
引入了一个奇怪的、模棱两可的句子“或以某种其他实现定义的方式”。这可以解释为“参数可能会有所不同”,也可以解释为“main 可以具有任何实现定义的形式”。int main()
一些编译者选择以后一种方式解释标准。可以说,人们不能轻易地通过引用标准本身来说明他们不符合标准,因为它是模棱两可的。
然而,允许完全狂野的形式可能不是这个新句子的意图。C99 理由(非规范性)意味着该句子指的是 4 的附加参数。main()
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 独立式环境
允许使用任何形式或名称的 main 8。
请注意,在上述任何版本中,从未将其列为任何托管 C 实现的有效形式。在 C 中,与 C++ 不同,具有不同的含义。前者是一个过时的功能,可能会从语言中删除。请参阅 C11 未来的语言方向:int main()
()
(void)
6.11.6 函数声明符
使用带有空括号的函数声明符(不是原型格式的参数类型声明符)是一个过时的功能。
- C++03 托管环境
允许的表格 9:
int main ()
int main (int argc, char *argv[])
评论:
请注意第一种形式的空括号。在这种情况下,C++ 和 C 是不同的,因为在 C++ 中,这意味着函数不带参数。但在 C 语言中,这意味着它可以接受任何参数。
- C++03 独立环境
启动时调用的函数的名称是实现定义的。如果它被命名,它必须遵循规定的表格 10:main()
// 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 独立环境
启动时调用的函数的名称是实现定义的。如果它被命名,它必须遵循规定的表格 12:main()
// 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] 必须是 null 指针,以便为列表末尾提供冗余检查,这也是基于常见做法。
main 是唯一可以用零个或两个参数可移植声明的函数。(其他函数的参数数量必须在调用和定义之间完全匹配。 这种特殊情况只是承认了当程序不访问程序参数字符串时将参数留给 main 的普遍做法。虽然许多实现支持两个以上的论点,但这种做法既不受标准的约束,也不受标准禁止;用三个参数定义 main 的程序是不严格合规的(参见 §J.5.1.)。
- ISO 9899:1999 5.1.2.2 托管环境 --> 5.1.2.2.3 程序终止
如果 main 函数的返回类型是与 int 兼容的类型,则从初始调用 main 函数返回的返回等效于以 main 函数返回的值作为其参数调用退出函数;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 主要功能
实现不应预定义 main 函数。此功能不得过载。它的返回类型应为 int 类型,但除此之外,其类型是实现定义的。所有实现都应允许以下两个 main 定义:
int main() { /* ... */ }
和
int main(int argc, char* argv[]) { /* ... */ }
- ISO 14882:2003 3.6.1 主要功能
它是否需要独立环境中的程序来定义主函数,这是由实现定义的。
- ISO 14882:2011 3.6.1 主要功能
实现不应预定义 main 函数。此功能不得过载。它的返回类型应为 int 类型,但除此之外,其类型是实现定义的。所有实现应 允许两者
— () 的函数,返回 int 和
— 返回 int 的函数 (int, pointer to pointer to char)
作为 main 的类型 (8.3.5)。
- ISO 14882:2011 3.6.1 主要功能
本节与上面引用的 C++ 相同。
评论
int my_startup_function ()
int my_startup_function (int argc, char *argv[])
char my_startup_function (long argc, int *argv[])
main()
void my_startup_function ()
main
func()
int main()
省略return 0
当一个 C 或 C++ 程序到达末尾时,编译器会自动生成返回 0 的代码,因此无需显式放在 的末尾。main
return 0;
main
注意:当我提出这个建议时,它几乎总是伴随着两种评论之一:“我不知道”或“这是糟糕的建议!我的基本原理是,依赖标准明确支持的编译器行为是安全且有用的。对于C,自C99起;请参阅 ISO/IEC 9899:1999 第 5.1.2.2.3 节:
[...]从对函数的初始调用返回等同于使用函数返回的值作为其参数来调用函数;达到终止函数将返回值 0。
main
exit
main
}
main
对于 C++,自 1998 年第一个标准以来;请参阅 ISO/IEC 14882:1998 第 3.6.1 节:
如果控件到达 main 的末尾而没有遇到 return 语句,则效果是执行 return 0;
从那时起,这两个标准的所有版本(C99和C++98)都保持了相同的思想。我们依赖于 C++ 中自动生成的成员函数,很少有人在函数末尾编写显式语句。反对省略的原因似乎可以归结为“看起来很奇怪”。如果你和我一样,对改用 C 标准的理由感到好奇,请阅读这个问题。另请注意,在 1990 年代初期,这被认为是“草率的做法”,因为它在当时是未定义的行为(尽管得到广泛支持)。return;
void
此外,C++ 核心准则在末尾包含多个省略实例,并且没有写入显式返回的实例。尽管该文件中尚未就这一特定主题制定具体准则,但这似乎至少是对这种做法的默许。return 0;
main
所以我主张省略它;其他人不同意(通常是激烈的!无论如何,如果你遇到省略它的代码,你就会知道它受到标准的明确支持,你就会知道它的含义。
评论
return 0;
return 0;
return 0;
return 0
在 C 和 C++ 中定义 main() 函数的正确(最有效)方法是什么——int main() 或 void main()——为什么?
“(最有效)”这几个字并不能改变问题。除非你处于一个独立的环境中,否则有一种普遍正确的声明方式,那就是返回。main()
int
C 和 C++ 应该返回什么?
main()
一个,纯粹而简单。这不仅仅是“应该归还的东西”,而是“必须归还的东西”。 当然,是其他人调用的函数。您无法控制调用 .因此,必须使用类型更正签名进行声明以匹配其调用方。在这件事上,你根本别无选择。你不必问自己什么效率更高或更低,或者什么风格更好或更差,或者类似的东西,因为答案已经完全定义了,对你来说,按照 C 和 C+ 标准。只要跟着他们走。int
main()
main()
main()
main
main
如果 int main() 则返回 1 还是返回 0?
0 表示成功,非零表示失败。同样,不是你需要(或得到)选择的东西:它是由你应该遵守的接口定义的。
在 C 中,C11 标准的第 5.1.2.2.1 节(强调我的):
它应使用
int
的返回类型定义,并且不定义 参数:int main(void) { /* ... */ }
或具有两个参数(此处称为 和 ,虽然 可以使用任何名称,因为它们是它们所在的函数的本地名称 声明):
argc
argv
int main(int argc, char *argv[]) { /* ... */ }
然而,对于像我这样的一些初学者来说,一个抽象的例子可以让我掌握它:
当你在程序中编写一个方法时,例如,作为该方法的调用者,你希望知道一切是否顺利(因为可能会发生故障,例如找不到文件)。通过检查方法的返回值,您可以知道一切是否顺利,这是该方法向您发出成功执行(或未执行)的信号的机制,并让调用者(例如,在您的主方法中)决定如何处理意外失败。int read_file(char filename[LEN]);
所以现在想象一下,我为一个微机制编写了一个 C 程序,它用于更复杂的系统。当系统调用微机制时,它想知道一切是否按预期进行,以便它可以处理任何潜在的错误。如果 C 程序的 main 方法返回 void,那么调用系统如何知道其子系统(微机制)的执行?它不能,这就是为什么 main() 返回 int,以便向其调用者传达成功(或未)执行的原因。
换言之:
理由是主机环境(即操作系统(OS))需要知道程序是否正确完成。如果没有 int 兼容类型作为返回类型(例如 void),则“返回到主机环境的状态未指定”(即大多数操作系统上的未定义行为)。
在 Windows 上,如果程序因访问冲突而崩溃,则退出代码将为 。与 x86 异常引起的其他类型的崩溃类似。STATUS_ACCESS_VIOLATION (0xC0000005)
因此,除了返回或传递的内容之外,还有其他内容可能会导致看到退出代码。main
exit
评论
main
ISO 现在强制要求 C 和 C++ 的 “int” 作为 “main” 的返回类型。
这两种语言以前都允许隐式“int”和“main”在没有任何返回类型的情况下声明。事实上,C++ 的第一个外部版本本身(1985 年 2 月的“cfront”版本 E)是用自己的语言编写的,声明为“main”,没有任何返回类型......但返回一个整数值:错误数或 127,以较小者为准
至于返回什么的问题:C 和 C++ 的 ISO 标准与 POSIX 标准同步工作。对于任何符合 POSIX 标准的托管环境,(1) 126 保留给操作系统的 shell 以指示不可执行的实用程序,(2) 127 保留给操作系统的 shell 以指示未找到的命令,(3) 实用程序的退出值在逐个实用程序的基础上单独说明,(4) 调用 shell 外部实用程序的程序应使用类似的值作为自己的出口,
(5) 值 128 及以上用于指示接收信号导致的终止,(6) 值 1-125 表示失败,
(7) 值 0 表示成功。
在 C 和 C++ 中,值 EXIT_SUCCESS 和 EXIT_FAILURE 用于处理最常见的情况:用于报告成功或只是一般失败的程序。它们可以(但不一定)分别等于 0 和 1。
这意味着,如果您希望程序为不同的故障模式或状态指示返回不同的值,同时继续使用这两个常量,您可能必须首先确保额外的“failure”或“status”值严格介于 max(EXIT_SUCCESS, EXIT_FAILURE) 和 126 之间(并希望中间有足够的空间), 并保留EXIT_FAILURE以标记通用或默认故障模式。
否则,如果你不打算使用常量,那么你应该按照 POSIX 的要求去做。
对于用于独立环境或不符合 POSIX 的主机的程序,我只能说,除了以下内容:
我编写了独立程序 - 作为自定义运行时系统上的多线程程序(以及其他所有内容的自定义工具库)。我遵循的一般规则是:
(1)“main”运行前台进程,通常只包括启动、配置或初始化例程,但也可以包括用于持续操作的前台进程(如轮询循环),
(2)“main”返回到无限的睡眠和等待循环中,(3)没有定义或使用“main”的返回值,
(4)后台进程作为中断驱动和事件驱动的线程单独运行,独立于“主”,仅通过接收到复位信号或其他线程终止。或者简单地关闭对驱动线程的任何事件的监视。
评论
main
main
#include
return
main(...)
void main()