I recently saw a warning while compiling C99 code that gave me pause to question if this is undefined behavior or not.
(since I build on various platforms and only an older compiler version shows this warning).
eg:
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);
}
With Clang 3.9.0 and GCC5.3 (on Linux) the code compiles without warnings.
However with Clang 3.4.1 (on FreeBSD), I get the following warning.
test.c:74:21: warning: variable 'test' is uninitialized when used within its own initialization [-Wuninitialized]
.y = test.x + 1
^~~~
The code above should be the equivalent of:
void func(float a) {
struct Vector test;
test.x = a;
test.y = test.x + 1;
printf("%f %f\n", test.x, test.y);
}
It may work in one compiler yet still be undefined behavior, so my question is:
Providing the order of initialization assigns members before use.
Does C99 struct initialization re-use members and guarantee a predictable outcome?
The proper syntax for self-referential initialization in this case would be
struct Vector test = { .y = a, .x = test.y + 1 };
Now, the language specification says
6.7.9 Initialization
23 The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.
While 6.7.9/19 does seem to establish temporal ordering on subobject initialization, this ordering does not in any way define the order the evaluation of individual initializer expressions. Initializer expressions can be evaluated out of order. So, it does not guarantee that test.y + 1 is evaluated after .y = a takes place. This means that your example is indeed undefined.
GCC expectedly produces a warning
'test.y' is used uninitialized in this function [-Wuninitialized]
MSVC reports
warning C4700: uninitialized local variable 'test' used
struct Vector { float x, y; };
struct Vector test = { .y = a, .x = .y + 1 };
That's a syntax error. The .x = (called a designator) must be followed by an initializer, which can be either an expression (specifically an assignment-expression) or a brace-enclosed initializer-list (with an optional trailing comma).
gcc 5.3.0 in particular reports this as a syntax error:
c.c: In function 'func':
c.c:6:41: error: expected expression before '.' token
struct Vector test = { .y = a, .x = .y + 1 };
as does clang 3.7.1:
c.c:6:41: error: expected expression
struct Vector test = { .y = a, .x = .y + 1 };
Your .y + 1 presumably is intended to be an expression, but it isn't. There is no valid syntax to refer to a member of the object currently being initialized.
If your compiler supports this, it's a language extension, and you'll need to consult your compiler's documentation to see how it works.
Reference: N1570 section 6.7.9.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With