标准 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) 或以其他一些实现定义的方式。
如果它们被声明,主函数的参数应遵循以下
约束:
-
argc 的值应为非负数。
-
argv[argc] 应为空指针。
- 如果
argc的值大于零,则数组成员argv[0]通过
argv[argc-1] inclusive 应包含指向字符串的指针,这些指针是给定的
在程序启动之前由主机环境实现定义的值。这
意图是向程序提供在程序启动之前确定的信息
来自托管环境中的其他地方。如果宿主环境不能
提供带有大写和小写字母的字符串,实现
应确保以小写形式接收字符串。
- 如果
argc的值大于零,则argv[0]指向的字符串
代表程序名称; argv[0][0] 应为空字符,如果
程序名称在主机环境中不可用。如果argc 的值为
大于一,argv[1] 到 argv[argc-1] 指向的字符串
表示程序参数。
-
argc 和argv 参数以及argv 数组指向的字符串应
可由程序修改,并在程序之间保留它们最后存储的值
启动和程序终止。
10) 因此,int 可以替换为定义为 int 的 typedef 名称,或者 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 被强制要求为“成功”。如果您愿意,可以使用 <stdlib.h> 中的 EXIT_FAILURE 和 EXIT_SUCCESS,但 0 已确定,1 也是如此。另请参阅 Exit codes greater than 255 — possible?。
在 C89 中(因此在 Microsoft C 中),没有说明如果 main() 函数返回但未指定返回值会发生什么;因此,它会导致未定义的行为。
7.22.4.4 exit 函数
¶5 最后,控制权返回到主机环境。如果status 的值为零或EXIT_SUCCESS,则返回状态成功终止 的实现定义形式。如果status 的值为EXIT_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] 作为指向空终止多字节字符串 (NTMBS) (17.5.2.1.4.2) 的初始字符的指针,argv[0] 应是指向代表 NTMBS 的初始字符的指针
用于调用程序的名称或""。 argc 的值应为非负数。 argv[argc]的值
应为 0。 [ 注意:建议在argv 之后添加任何其他(可选)参数。 -结尾
注意]
¶3 函数main 不得在程序中使用。 main 的链接(3.5)是实现定义的。 [...]
¶5 main 中的 return 语句具有离开 main 函数的效果(使用自动销毁任何对象)
存储持续时间)并以返回值作为参数调用std::exit。如果控制到达终点
main没有遇到return语句,效果就是执行
return 0;
C++ 标准明确规定“它 [主函数] 的返回类型应为 int,否则它的类型是实现定义的”,并且需要与 C 标准相同的两个签名作为选项支持.因此,C++ 标准直接不允许使用“void main()”,尽管它无法阻止允许替代方案的非标准实现。请注意,C++ 禁止用户调用 main(但 C 标准没有)。
C++11 标准中的第 18.5 节 开始和终止 段落与第 7.22.4.4 节 exit 函数 中的段落相同C11 标准(上面引用),除了一个脚注(它只是记录了 EXIT_SUCCESS 和 EXIT_FAILURE 在 <cstdlib> 中定义)。
标准 C — 通用扩展
传统上,Unix 系统支持第三种变体:
int main(int argc, char **argv, char **envp) { ... }
第三个参数是一个以空结尾的字符串指针列表,每个字符串都是一个环境变量,它有一个名称、一个等号和一个值(可能为空)。如果你不使用它,你仍然可以通过'extern char **environ;'进入环境。这个全局变量在 POSIX 中是独一无二的,因为它没有声明它的标头。
这被 C 标准认可为通用扩展,记录在附件 J 中:
J.5.1 环境参数
¶1 在托管环境中,主函数接收第三个参数,char *envp[],
指向一个以空结尾的指向char 的指针数组,每个指针都指向一个字符串
提供有关此程序执行环境的信息 (5.1.2.2.1)。
微软 C
Microsoft VS 2010 编译器很有趣。该网站说:
main的声明语法是
int main();
或者,可选地,
int main(int argc, char *argv[], char *envp[]);
或者,main 和 wmain 函数可以声明为返回 void(无返回值)。如果将main 或wmain 声明为返回void,则不能使用return 语句将退出代码返回给父进程或操作系统。要在 main 或 wmain 声明为 void 时返回退出代码,您必须使用 exit 函数。
我不清楚当带有void main() 的程序退出时会发生什么(将退出代码返回给父级或操作系统)——并且 MS 网站也静默。
有趣的是,MS 没有规定 C 和 C++ 标准要求的 main() 的两个参数版本。它只规定了一个三参数形式,其中第三个参数是char **envp,一个指向环境变量列表的指针。
Microsoft 页面还列出了一些其他替代方案——wmain(),它采用宽字符串等等。
this page 的 Microsoft Visual Studio 2005 版本未将 void main() 列为替代项。来自 Microsoft 的 versions Visual Studio 2008 及以上。
标准 C — 独立环境
如前所述,上述要求适用于托管环境。如果您使用的是独立环境(这是托管环境的替代方案),那么标准就没有什么可说的了。对于独立环境,程序启动时调用的函数不需要调用main,并且对其返回类型没有限制。标准说:
5.1.2 执行环境
定义了两种执行环境:独立和托管。在这两种情况下,
当执行调用指定的 C 函数时,程序启动发生
环境。所有具有静态存储持续时间的对象都应在程序启动之前进行初始化(设置为其初始值)。这种初始化的方式和时间是未指定的。程序终止将控制权返回给执行环境。
5.1.2.1 独立环境
在独立环境中(C 程序的执行可能在没有任何操作系统优势的情况下发生),程序启动时调用的函数的名称和类型是实现定义的。除了第 4 条要求的最小集合之外,独立程序可用的任何库设施都是实现定义的。
独立环境中程序终止的效果是由实现定义的。
第 4 条一致性的交叉引用指的是:
¶5严格符合程序应仅使用本国际标准中指定的语言和库的那些功能。3)它不应产生依赖于任何未指定的输出、未定义或实现定义的行为,并且不得超过任何最小实现限制。
¶6 两种形式的一致性实现是hosted 和freestanding。 符合要求的托管实现应接受任何严格符合要求的程序。 一致的独立实现应接受任何严格一致的程序,其中库条款(第 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> 版本应至少声明函数 abort、atexit、at_quick_exit、exit 和 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>
在 C 中使用 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 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;
}