-->

静态初始化顺序的悲剧静态初始化顺序的悲剧(static initialization order f

2019-05-10 12:52发布

我是从一本书阅读SIOF它举了一个例子:

//file1.cpp
extern int y;
int x=y+1;

//file2.cpp
extern int x;
int y=x+1;  

现在,我的问题是:
在上面的代码中,将下面的事情发生吗?

  1. 在编译file1.cpp,编译叶ÿ因为它即不为它分配存储空间。
  2. 编译器为X分配存储空间,但不会对其进行初始化。
  3. 虽然编译file2.cpp,编译叶X,因为它即不为它分配存储空间。
  4. 编译器y的分配存储空间,但不会对其进行初始化。
  5. 虽然联file1.o和file2.o,现在就让file2.o首先被初始化,所以现在:
    请问X得到的0初始值? 或者没有得到初始化?

Answer 1:

在初始化步骤中给出3.6.2 C ++标准的“非本地对象的初始化”:

步骤1: xy是零初始化任何其它初始化发生之前。

步骤2: xy动态初始化-其中之一是由标准指定。 该变量将获得价值1 ,因为其他变量已经初始化为零。

步骤3:将其它变量将被动态地初始化,获取值2



Answer 2:

SIOF是非常多的运行时神器,编译器和连接器没有太多用它做。 考虑的atexit()函数,它注册功能中的程序退出被调用。 许多CRT实现对程序初始化类似的东西,让我们atinit称之为()。

初始化这些全局变量,需要执行的代码,该值不能由编译器来决定。 因此,编译器生成的,其执行的表达,并将值机器代码段。 这些片段需要的main()运行之前被执行。

这就是atinit()开始发挥作用。 一个常见的CRT实现行走atinit函数指针的列表,并执行初始化片段,为了。 的问题是,其中功能在atinit()列表中注册的顺序。 而atexit对()具有良好定义的LIFO顺序,它是隐式其中代码调用atexit(的顺序确定),例如不是atinit功能的情况。 语言规范并不要求一个订单,没有什么,你可以在你的代码做指定定单。 SIOF是结果。

一种可能的实现是编译器在一个单独的部分发射函数指针。 链接器将它们合并,产生atinit列表。 如果你的编译器是那么初始化顺序将通过您的链接目标文件的顺序来确定。 看看地图文件,你应该看到atinit节,如果你的编译器来实现的。 它不会被称为atinit,但某种与“初始化”的名字是可能的。 纵观在调用的main()应该深入了解以及在CRT源代码。



Answer 3:

整点(和它被称为“惨败”的原因)是,它是不可能有任何把握在这样的情况下会发生什么的话。 从本质上讲,你问什么是不可能的(即每两个变量是其中一个比另一个更大)。 由于他们不能做到这一点,他们需要做的是开放的一些问题 - 他们可能会产生0/1或1/0或1/2或2/1,或可能(最好的情况下)只是一个错误信息。



Answer 4:

这是编译器相关的,并且可以运行时依赖。 编译器可以决定懒惰地初始化静态变量时在一个文件中的第一变量被访问,或者作为每个变量被访问。 否则,它会在启动时的文件初始化所有静态变量,随着订单通常根据文件的链接顺序。 该文件顺序可能会更改基于相关性或其他,编译器相关的影响。

静态变量通常初始化为零,除非他们有一个常量初始化。 再次,这是编译器相关的。 因此,这些变量中的一个可能会是零,当其他被初始化。 然而,因为两者具有初始化一些编译器可能会离开的值不确定。

我认为最可能的情况是:

  1. 将空间分配给变量,并且两者都具有值0。
  2. 一个变量,表示X,被初始化并设置为值1。
  3. 另外,说Y,被初始化并设置为值2。

你总是可以运行它,看看。 这可能是一些编译器将生成的代码进入一个无限循环。



文章来源: static initialization order fiasco