在 C++ 中,用自身初始化全局变量是否有未定义的行为?

In C++, does initializing a global variable with itself have undefined behaviour?

int i = i;

int main() { 
 int a = a;
 return 0;
} 

int a = a 肯定有未定义的行为(UB),关于它的更多细节在 .

但是 int i = i 呢?在 C++ 中,我们可以将非常量值分配给全局变量。 i 在遇到声明之前已声明并初始化为零(因为它具有文件范围)。在这种情况下,我们稍后在定义中将 0 分配给它。 可以说这没有 UB 吗?

令人惊讶的是,这不是未定义的行为。

Static initialization [basic.start.static]

Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized. If constant initialization is not performed, a variable with static storage duration or thread storage duration is zero-initialized. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before any dynamic initialization.

重要部分加粗。 “静态初始化”包括全局变量初始化,“静态存储持续时间”包括全局变量,以上条款适用于此:

int i = i;

这不是常量初始化。因此,零初始化是根据上述子句完成的(对于基本整数类型,零初始化意味着,毫不奇怪,它被设置为 0)。上面的条款还规定零初始化必须发生在动态初始化之前。

所以,这里发生了什么:

  1. i初始化为0.
  2. i 然后从自身动态初始化,所以它仍然是 0。

i 的行为可能未定义,因为根据您阅读标准的方式,您可能会在其生命周期开始前阅读 i

[basic.life]/1.2

... The lifetime of an object of type T begins when:

— its initialization (if any) is complete ...

如另一个答案中所述,i 被初始化两次:首先静态零初始化,然后用 i 动态初始化。

生命周期从哪个初始化开始?第一个还是最后一个?

标准含糊不清,其中有相互矛盾的注释(尽管都是are non-normative). Firstly, there is a footnote in [basic.life]/6 (thanks @eerorika)明确表示动态初始化开始生命周期:

[basic.life]/6

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated26

...

26) For example, before the dynamic initialization of an object with static storage duration ...

这个解释对我来说最有意义,否则它会 在动态之前访问 class 个实例是合法的 初始化,然后才能建立其不变量(包括标准定义的标准库 classes)。

还有一个conflicting note in [basic.start.static]/3,但是那个比我上面提到的那个要旧

在我看来 int i = i; 有未定义的行为,不是由不确定的值引起的。术语不确定值专为具有 自动或动态存储持续时间 .

的对象而设计

[basic.indet#1]

When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced ([expr.ass]).

[basic.indet#2]

If an indeterminate value is produced by an evaluation, the behavior is undefined except in the following cases...

在您的示例中,名为i 的对象具有静态存储持续时间,因此不在谈论不确定值的范围内。而且,这样的对象有一个零初始化,发生在任何动态初始化之前 [basic.start.static#2]

Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before ([intro.races]) any dynamic initialization.

因此,它的初始值为零。当 i 用作初始化器来初始化自身时。这是一个动态初始化,它遵循 [dcl.init].

Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression.

违反了[basic.lifetime]

中的规则

The program has undefined behavior if:

  • the glvalue is used to access the object, or