C标准中的$ 3.6.1 / 1节,
程序应包含一个名为main的全局函数,它是程序的指定开始 .
现在考虑这段代码,
int square(int i) { return i*i; }
int user_main()
{
for ( int i = 0 ; i < 10 ; ++i )
std::cout << square(i) << endl;
return 0;
}
int main_ret= user_main();
int main()
{
return main_ret;
}
这个示例代码完成了我打算做的事情,即打印从0到9的整数平方, before 进入 main()
函数,该函数应该是程序的"start" .
我还用 -pedantic
选项,GCC 4.5.0编译了它 . 它没有错误,甚至没有警告!
所以我的问题是,
Is this code really Standard conformant?
如果它符合标准,那么它是否会使标准所说的无效? main()
不是这个程序的开始! user_main()
在 main()
之前执行 .
我知道要初始化全局变量 main_ret
, use_main()
首先执行,但这是完全不同的事情;关键是, does 使标准中的引用语句$ 3.6.1 / 1无效,因为 main()
不是程序的开头;事实上这是该计划的 end !
编辑:
如何定义“开始”这个词?
它归结为短语"start of the program"的定义 . 那么你究竟如何定义呢?
11 回答
您的“程序”只是从全局变量返回一个值 . 其他一切都是初始化代码 . 因此,标准成立 - 你只需要一个非常简单的程序和更复杂的初始化 .
main()是由C运行时库调用的用户函数 .
另见:Avoiding the main (entry point) in a C program
看起来像英语语义狡辩 . OP首先将他的代码块称为“代码”,然后将其称为“程序” . 用户编写代码,然后编译器编写程序 .
在初始化所有全局变量之后调用main .
标准未指定的是所有模块和静态链接库的所有全局变量的初始化顺序 .
是的,main是每个C程序的“切入点”,除了特定于实现的扩展 . 即便如此,有些事情发生在主要的,特别是全局初始化之前,例如main_ret .
不,C在调用main之前做了很多事情来“设置环境”;但是,main是C程序中“用户指定”部分的正式启动 .
一些环境设置是不可控的(比如设置std :: cout的初始代码;但是,某些环境可以像静态全局块一样控制(用于初始化静态全局变量) . 注意,因为你没有完整在main之前控制,您无法完全控制静态块初始化的顺序 .
在main之后,您的代码在概念上“完全控制”程序,在某种意义上,您既可以指定要执行的指令,也可以指定执行它们的顺序 . 多线程可以重新排列代码执行顺序;但是,你仍然可以控制C,因为你指定让代码段执行(可能)乱序 .
你正在错误地阅读这句话 .
该标准是为了标准的其余部分而定义"start"这个词 . 它并没有说在调用
main
之前没有代码执行 . 它表示程序的开头被认为是函数main
.您的计划符合要求 . 在main启动之前,您的程序还没有"started" . 根据标准中"start"的定义,在程序"starts"之前调用构造函数,但这几乎不重要 . 在每个程序中调用
main
之前执行很多代码,而不仅仅是这个例子 .For the purposes of discussion, your constructor code is executed prior to the 'start' of the program, and that is fully compliant with the standard.
除非有主程序,否则您的程序将不会链接,因此不会运行 . 但是main()不会导致程序执行的开始,因为文件级别的对象具有预先运行的构造函数,并且可以在main()到达之前编写一个运行其生命周期的整个程序,并让main本身具有一个空的身体 .
实际上要强制执行此操作,您必须拥有一个在main及其构造函数之前构造的对象来调用程序的所有流程 .
看这个:
你的程序流程实际上源于
Foo::Foo()
您也将问题标记为“C”,然后,严格地说C,您的初始化应该按照ISO C99标准的第6.7.8节“初始化”失败 .
在这种情况下最相关的似乎是约束#4,它说:
因此,您的问题的答案是代码不符合C标准 .
如果您只对C标准感兴趣,可能需要删除“C”标记 .
第3.6节作为一个整体非常清楚
main
与动态初始化的相互作用 . "designated start of the program"在其他任何地方都没有使用,只是描述main()
的一般意图 . 以规范的方式解释一个短语与标准中更详细和明确的要求相矛盾是没有任何意义的 .编译器通常必须在main()之前添加代码以符合标准 . 因为该标准规定必须在程序执行之前完成全局/静态的初始化 . 如上所述,对于放置在文件范围(全局)的对象的构造函数也是如此 .
因此,原始问题也与C相关,因为在C程序中,您仍然可以在程序启动之前执行全局/静态初始化 .
标准假设这些变量是通过"magic"初始化的,因为它们没有说明在程序初始化之前应该如何设置它们 . 我认为他们认为这是一种超出编程语言标准范围的东西 .
编辑:参见例如ISO 9899:1999 5.1.2:
关于如何完成这种“神奇”的理论可以追溯到C的诞生,当时它是一种仅用于UNIX OS的编程语言,基于RAM的计算机 . 从理论上讲,程序将能够将所有预先初始化的数据从可执行文件加载到RAM中,同时程序本身被上传到RAM .
从那时起,计算机和操作系统已经发展,并且C用于比原先预期的更广泛的区域 . 现代PC OS具有虚拟地址等,并且所有嵌入式系统都从ROM而不是RAM执行代码 . 因此,有许多情况下RAM无法“自动”设置 .
此外,标准过于抽象,无法了解堆栈和进程内存等 . 在程序启动之前,这些事情也必须完成 .
因此,几乎每个C / C程序都有一些在调用main之前执行的init /“copy-down”代码,以符合标准的初始化规则 .
例如,嵌入式系统通常有一个称为“非ISO兼容启动”的选项,其中出于性能原因跳过整个初始化阶段,然后代码实际上直接从main启动 . 但是这样的系统不符合标准,因为你不能依赖全局/静态变量的init值 .