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

Dan*_*Dan 64 c++ undefined-behavior language-lawyer

int i = i;

int main() { 
 int a = a;
 return 0;
} 
Run Code Online (Sandbox Code Playgroud)

int a = a肯定有未定义的行为 (UB),关于它的更多细节在 读取未初始化的值总是未定义的行为吗?或者有例外吗?.

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

Sam*_*hik 57

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

静态初始化 [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.

Important parts bold-faced. "Static initialization" includes global variable initialization, "static storage duration" includes global variables, and the above clause is applicable here:

int i = i;
Run Code Online (Sandbox Code Playgroud)

This is not constant-initialization. Therefore, zero-initialization is done according to the above clause (for basic integer types zero-initialization means, unsurprising, that it's set to 0). The above clause also specifies that zero initialization must take place before dynamic initialization.

So, what happens here:

  1. i is initialized to 0.
  2. i is then dynamically initialized, from itself, so it still remains 0.

  • 那真好笑。这可以让您创建一个不可构造的对象!`struct S { S() = 删除; } s = s;` (37认同)
  • @RaymondChen 实际上对于 C++20 之前的无捕获 lambda 很有用,它没有默认构造函数:/sf/answers/3990880981/。 (9认同)
  • 全局别名会如何改变?`int & i = i;` (2认同)

Hol*_*Cat 5

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

\n
\n

[basic.life]/1.2

\n

...类型 T 的对象的生命周期从以下时间开始:

\n

\xe2\x80\x94 其初始化(如果有)已完成...

\n
\n

正如另一个答案中提到的,i初始化两次:首先静态零初始化,然后i动态初始化。

\n

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

\n

该标准含糊不清,并且其中存在相互矛盾的注释(尽管它们都是非规范性的)。[basic.life]/6首先, (感谢@eerorika )中有一个脚注明确指出动态初始化启动生命周期:

\n
\n

[basic.life]/6

\n

在对象的生命周期开始之前但在对象将占用的存储空间已分配之后26

\n

...

\n

26) 例如,在动态初始化具有静态存储持续时间的对象之前...

\n
\n

这种解释对我来说最有意义,因为否则在类实例进行动态初始化、建立其不变量(包括标准定义的标准库类)之前访问类实例是合法的。

\n

中还有一个相互矛盾的注释[basic.start.static]/3,但该注释比我上面提到的更早。

\n

  • @Dan 这个问题是关于 C 和 `int i; i = i;` 在命名空间范围内甚至不合法。 (4认同)