静态初始化命令失败
我从一本书中读到了SIOF,它举了一个例子:
//file1.cpp extern int y; int x=y+1; //file2.cpp extern int x; int y=x+1;
现在我的问题是:
在上面的代码中,会发生下面的事情吗?
- 当编译file1.cpp时,编译器会保留y,即不为其分配存储空间。
- 编译器为x分配存储空间,但不会初始化它。
- 当编译file2.cpp时,编译器会将x保留,即不为其分配存储空间。
- 编译器为y分配存储空间,但不会初始化它。
- 当链接file1.o和file2.o时,现在让file2.o被初始化,所以现在:
x是否获得0的初始值? 或没有得到初始化?
初始化步骤在C ++标准的3.6.2“初始化非本地对象”中给出:
步骤1:在任何其他初始化发生之前, x
和y
都是零初始化的。
步骤2: x
或y
是dynamic初始化的 – 哪一个是标准未指定的。 该variables将得到值1
因为另一个variables将被初始化为零。
第三步:其他variables将被dynamic初始化,得到值2
。
SIOF是一个非常运行时的工件,编译器和链接器与它没有多大关系。 考虑atexit()函数,它注册要在程序退出时调用的函数。 许多CRT实现有类似的程序初始化,我们称之为atinit()。
初始化这些全局variables需要执行代码,该值不能由编译器确定。 所以编译器生成执行expression式并赋值的机器代码片段。 这些代码片段需要在main()运行之前执行。
这就是atinit()的作用。 一个普通的CRT实现按顺序遍历atinit函数指针列表并执行初始化片段。 问题是函数在atinit()列表中注册的顺序。 虽然atexit()具有良好定义的LIFO顺序,并且由代码调用atexit()的顺序隐式确定,但atinit函数并不是这样。 语言规范不需要订单,您可以在代码中指定订单。 SIOF是结果。
一个可能的实现是编译器在一个单独的章节中发出函数指针。 链接器将它们合并,生成atinit列表。 如果你的编译器这样做,那么初始化顺序将由链接目标文件的顺序决定。 看看地图文件,如果你的编译器这样做的话,你应该看到atinit部分。 它不会被称为atinit,但有一些与“init”的名称是可能的。 看一下调用main()的CRT源代码也应该给出一些洞见。
整个观点(以及被称为“惨败”的原因)在于,无法确定地说出这样的情况会发生什么 。 本质上,你要求的东西不可能(两个variables都比另一个大)。 既然他们不能这样做,他们会做什么是开放的一些问题 – 他们可能会产生0/1,或1/0,或1/2,或2/1,或可能(最好的情况下)只是一个错误信息。
它依赖于编译器,可能依赖于运行时。 当访问文件中的第一个variables或访问每个variables时,编译器可能会决定懒惰地初始化静态variables。 否则,它将在启动时通过文件初始化所有静态variables,顺序通常取决于文件的链接顺序。 文件顺序可以根据依赖关系或其他依赖于编译器的影响而改变。
静态variables通常被初始化为零,除非它们有一个常量初始化器。 再次,这是编译器依赖。 所以当其他variables初始化时,其中一个variables可能为零。 但是,由于两者都有初始化器,所以一些编译器可能会使值不确定。
我认为最可能的情况是:
- 空间分配给variables,并且都具有值0。
- 一个variables,比如x,被初始化并设置为1。
- 另一个例如y被初始化并设置为值2。
你总是可以运行它,看看。 可能有些编译器会生成进入无限循环的代码。