And*_*ton 2 c initialization c99 designated-initializer c11
GCC 和 Clang 都允许指定的初始化器引用正在初始化的结构或数组的成员,但是这是合法且定义良好的行为吗?
以下代码示例针对 GCC 和 Clang 进行编译和运行,并{ .a = 3, .b = 6, }在两种情况下输出:
#include <stdio.h>
typedef struct
{
int a;
int b;
} foo;
int main()
{
foo bar = {
.a = 3,
.b = bar.a + 3,
};
printf("{ .a = %d, .b = %d, }\n", bar.a, bar.b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
GCC 为指定的初始化生成以下输出(编译器资源管理器链接),这表明该示例的操作是安全的:
mov dword ptr [rbp - 4], 0
mov dword ptr [rbp - 16], 3
mov eax, dword ptr [rbp - 16]
add eax, 3
mov dword ptr [rbp - 12], eax
Run Code Online (Sandbox Code Playgroud)
C99 规范草案的第 6.7.8 节对此进行了讨论,但我不明白它如何以某种方式定义此行为。
特别是,第 19 点表明初始化按指定的顺序发生,但第 23 点提到了具有未指定顺序的副作用。我不确定写入结构的数据是否被视为副作用。
- 初始化应按初始值设定项列表顺序进行,为特定子对象提供的每个初始值设定项将覆盖同一子对象的任何先前列出的初始值设定项;所有未显式初始化的子对象应与具有静态存储持续时间的对象一样隐式初始化。
- 初始化列表表达式中任何副作用发生的顺序未指定
您引用的是旧版本的 C 标准。目前的草案(自 C11 起)对于第 23 点有:
初始化列表表达式的计算相对于彼此是不确定地排序的,因此任何副作用发生的顺序是未指定的。
我认为这意味着编译器可以选择在使用特定初始化表达式之前的任何时间评估该表达式,这意味着它可能发生在它引用的元素初始化之前或之后。
在这种情况下,在初始化表达式中使用同一聚合对象的(可能)未初始化的元素必定会导致不确定的值。