具有静态存储持续时间的常量初始化变量的初始化顺序

时间:2019-10-21 03:01:38

标签: c++ initialization language-lawyer static-initialization

基于以下代码段:

const int a = 42;
const int b = a;

我们知道这两个变量都进行了常量初始化,并且常量初始化是静态初始化。

由于未指定静态初始化的顺序(与指定了顺序的动态初始化不同),这将不会导致不确定的行为,就像b的静态初始化发生在a之前一样,它会读取未初始化的内存吗?

3 个答案:

答案 0 :(得分:1)

首先,让我们澄清一下上下文:我们正在考虑对具有 static 存储时间的变量进行初始化。该初始化实际上有两个部分:静态阶段和动态阶段。静态阶段首先发生,并且在此阶段变量之间没有依赖关系。初始化的顺序仅对具有静态存储的变量的动态初始化重要。

静态变量(动态)初始化的顺序通常不是不确定的。通常,在单独的翻译单元之间,或者在某些情况下,在单个翻译单元中,是未指定的。

const int a = dynamic_init();
const int b = a;

如果如示例所示,这些变量位于单个TU中,则指定顺序:a首先声明,因此首先初始化。

const int a = 42;
const int b = a;

42是一个常量表达式。因此,a具有恒定的初始化,这是静态的初始化(不是动态的)。因此,初始化顺序对该变量无关紧要。

a也是一个常量表达式,因为aconst变量,具有静态存储和常量初始化程序。因此b也具有非动态初始化。因此,初始化顺序对该变量无关紧要。


与上下文无关:

  

由于静态初始化的顺序未指定[...],所以这不会导致不确定的行为,就像b的静态初始化发生在a之前一样。

仅仅因为standard没有指定顺序,并不意味着实现可以做任何想要的事情。 Standard表示发生了静态初始化,并且实现负责执行它。它必须选择一个符合指定要求的订单。

在实践中,顺序并不重要,因为程序无法观察到它。无论实现如何选择实现,这两个变量都初始化为42。

答案 1 :(得分:0)

  

由于未指定静态初始化的顺序[…],这是否可能导致未定义的行为,就像b的静态初始化发生在a之前一样,它将读取未初始化的内存?

[conv.lval]/2不允许“读取内存”(已初始化或未初始化):

  

将左值到右值转换应用于表达式e时,两者之一
  — e可能无法评估,或者
  —对e的求值导致对ex的一组潜在结果的成员e的求值,并且ex将变量x命名为未被ex使用,
  所引用的对象中包含的值未被访问

(我想我不需要证明a的初始化程序没有使用b

答案 2 :(得分:0)

由[intro.execution]指定

这两个声明的完整表达式分别是它们的 init-declarators。根据 [intro.execution#5]

<块引用>

一个完整的表达式是。

<块引用>
  • 一个 init-declarator ([dcl.decl]) 或一个 mem-initializer ([class.base.init]),包括初始化器的组成表达式。

关联的初始化被视为其完整表达式的一部分

<块引用>

对于初始化程序,执行实体的初始化(包括评估聚合的默认成员初始化程序)也被视为完整表达式的一部分。

因此,它们的评估顺序由以下定义:

<块引用>

与完整表达式相关的每个值计算和副作用每个与要评估的下一个完整表达式相关的值计算和副作用之前排序。