main()真的是C ++程序的开始吗?
来自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的整数的平方, 然后进入main()
函数,该函数应该是程序的“开始”。
看看这里的输出: http : //www.ideone.com/Niy0R
我还用-pedantic
选项GCC 4.5.0编译了它。 它没有错误,甚至没有警告!
所以我的问题是,
这个代码真的符合标准吗?
如果它符合标准,那么它是不是使标准所说的无效? main()
不是这个程序的开始! user_main()
在main()
之前执行。
我明白要初始化全局variablesmain_ret
, use_main()
首先执行,但完全不同; 重点是,它确实使标准中引用的语句$ 3.6.1 / 1无效,因为main()
不是程序的开始 ; 这其实是这个计划的结束 !
编辑:
你如何定义“开始”这个词?
归结到“程序开始”这个短语的定义。 那么你究竟如何定义呢?
不,C ++在调用main之前会做很多事情来“设置环境”。 然而,main是C ++程序的“用户指定”部分的正式开始。
一些环境设置是不可控制的(就像设置std :: cout的初始代码一样;但是,一些环境是可以像静态全局块一样控制的(用于初始化静态全局variables)注意,由于没有满在main之前的控制,你没有完全控制静态块初始化的顺序。
在main之后,你的代码在概念上是“完全控制”程序的,因为你可以指定要执行的指令以及执行它们的顺序。 multithreading可以重新安排代码执行顺序; 但是,由于您指定了代码段(可能)不按顺序执行,因此您仍然控制着C ++。
您正在阅读错误的句子。
程序应该包含一个名为main的全局函数, 它是程序的指定开始。
该标准是为了该标准的其余部分的目的而定义“开始”一词。 它没有说在调用main
之前没有执行代码。 它说,该计划的开始被认为是在function的main
。
你的程序是兼容的。 在main启动之前,你的程序还没有“开始”。 在程序“开始”之前,根据标准中“start”的定义调用构造函数,但这并不重要。 在每个程序中调用main
之前执行很多代码,而不仅仅是这个例子。
为了讨论,您的构造函数代码在程序“开始”之前执行,并且完全符合标准。
你的程序不会链接,因此不会运行,除非有一个主要的。 然而,main()不会导致程序执行的开始,因为在文件级别的对象具有预先运行的构造函数,并且有可能在main()到达之前编写一个运行其整个生命周期的程序,让main本身一个空的身体。
实际上,为了执行这个操作,你必须有一个在main和它的构造函数之前构造的对象来调用程序的所有stream程。
看这个:
class Foo { public: Foo(); // other stuff }; Foo foo; int main() { }
你的程序的stream程将有效地源自Foo::Foo()
您也将问题标记为“C”,那么严格讲C,您的初始化将会按照ISO C99标准的第6.7.8节“初始化”进行失败。
在这种情况下最相关的似乎是约束#4,它说:
对于具有静态存储持续时间的对象,初始化器中的所有expression式应该是常量expression式或string文字。
所以,你的问题的答案是代码不符合C标准。
如果您只对C ++标准感兴趣,您可能需要删除“C”标签。
第3.6节作为一个整体,对main
和dynamic初始化的相互作用非常清楚。 程序的“指定开始”不在其他地方使用,只是描述main()
的一般意图。 以规范的方式来解释这一个短语与标准中较为详细和明确的要求相矛盾是毫无意义的。
编译器通常必须在main()之前添加代码才能符合标准。 因为这个标准规定了全局/静态的初始化必须在程序执行之前完成。 如前所述,放置在文件范围(全局variables)的对象的构造函数也是如此。
因此,原来的问题也与C相关,因为在C程序中,在程序启动之前,您仍然可以进行全局/静态初始化。
标准假定这些variables是通过“魔术”初始化的,因为他们没有说在程序初始化之前应该如何设置它们。 我认为他们认为这不在编程语言标准的范围之内。
编辑:例如见ISO 9899:1999 5.1.2:
在程序启动之前,所有具有静态存储持续时间的对象都应该被初始化(设置为它们的初始值)。 这种初始化的方式和时间在其他方面是不确定的。
这个“魔法”如何实现的理论可以追溯到C的诞生,当时它是一种旨在仅用于UNIX操作系统的编程语言,在基于RAM的计算机上。 从理论上讲,程序能够将程序本身上传到RAM的同时,将可执行文件中的所有预初始化数据加载到RAM中。
从那以后,计算机和操作系统已经发展起来,C的使用范围比原先预想的要大得多。 一个现代的PC操作系统具有虚拟地址等,所有embedded式系统都从ROM执行代码,而不是RAM。 所以有很多情况下RAM不能“自动”设置。
而且,这个标准太抽象了,无法了解堆栈和进程内存等。这些事情也必须在程序启动之前完成。
因此,几乎每个C / C ++程序都有一些在调用main之前执行的init /“copy-down”代码,以符合标准的初始化规则。
举个例子,embedded式系统通常有一个叫做“非ISO兼容启动”的选项,出于性能的原因整个初始化阶段被跳过,然后代码实际上直接从main开始。 但是这样的系统不符合标准,因为你不能依赖全局/静态variables的init值。
你的“程序”只是从一个全局variables中返回一个值。 其他一切都是初始化代码。 这样,标准就成立了 – 你只需要一个非常简单的程序和更复杂的初始化。
main()是C运行时库调用的用户函数。
另请参阅: 避免C程序中的主要(入口点)
看起来像一个英语语义学的狡辩。 OP将其代码块首先称为“代码”,随后称为“程序”。 用户编写代码,然后编译器编写程序。
是的,main是每个C ++程序的“入口点”,除了特定于实现的扩展外。 即便如此,有些事情发生在main之前,特别是main_ret之类的全局初始化。
main初始化所有的全局variables后调用。
标准没有指定的是所有模块和静态链接库的所有全局variables的初始化顺序。