在初始化中重新使用结构自己的成员是否可靠? (C99)

时间:2016-02-18 20:12:49

标签: c c99

我最近在编译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初始化是否重用成员并保证可预测的结果?

2 个答案:

答案 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节。