我最近在编译C99代码时看到了一个警告,让我暂停询问这是否是未定义的行为。
(因为我构建在各种平台上,只有较旧的编译器版本显示此警告)。
例如:
struct Vector { float x, y; };
void func(float a) {
struct Vector test = { .x = a, .y = test.x + 1 };
printf("%f %f\n", test.x, test.y);
}
使用Clang 3.9.0和GCC5.3(在Linux上),代码编译时没有警告。
但是对于Clang 3.4.1(在FreeBSD上),我收到以下警告。
test.c:74:21: warning: variable 'test' is uninitialized when used within its own initialization [-Wuninitialized]
.y = test.x + 1
^~~~
上面的代码应该相当于:
void func(float a) {
struct Vector test;
test.x = a;
test.y = test.x + 1;
printf("%f %f\n", test.x, test.y);
}
它可能在一个编译器中工作但仍然是未定义的行为,所以我的问题是:
提供初始化顺序在使用前指定成员
C99 struct
初始化是否重用成员并保证可预测的结果?
答案 0 :(得分:4)
在这种情况下,自引用初始化的正确语法是
struct Vector test = { .y = a, .x = test.y + 1 };
现在,语言规范说
6.7.9初始化
23 初始化列表表达式的评估是 不确定地相互之间进行排序,从而对 发生任何副作用的顺序是未指定的。
虽然6.7.9 / 19似乎确实在子对象初始化上建立了时间顺序,但这种排序不会以任何方式定义单个初始化表达式的评估顺序。可以不按顺序评估初始化表达式。因此,它不能保证在test.y + 1
发生后评估.y = a
。这意味着您的示例确实未定义。
GCC预计会发出警告
'test.y' is used uninitialized in this function [-Wuninitialized]
MSVC报告
warning C4700: uninitialized local variable 'test' used
答案 1 :(得分:3)
struct Vector { float x, y; };
struct Vector test = { .y = a, .x = .y + 1 };
这是一个语法错误。 .x =
(称为指示符)后面必须跟一个初始化程序,它可以是一个表达式(特别是赋值表达式)或括号括起来的初始化列表(带有可选的尾随逗号)。
gcc 5.3.0特别报告此语法错误:
c.c: In function 'func':
c.c:6:41: error: expected expression before '.' token
struct Vector test = { .y = a, .x = .y + 1 };
clang 3.7.1:
c.c:6:41: error: expected expression
struct Vector test = { .y = a, .x = .y + 1 };
你的.y + 1
大概是为了表达,但它不是。没有有效的语法来引用当前正在初始化的对象的成员。
如果您的编译器支持此功能,那么它是一种语言扩展,您需要查阅编译器的文档以了解其工作原理。
参考:N1570第6.7.9节。