The standard says…
I'm going to quote from §6.7.9 Initializers of ISO/IEC 9899:2011 (the C11 standard), the same section as Vlad from Moscow quotes in his answer:
¶16 Otherwise, the initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members.
¶17 Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union.148) In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator. Initialization then continues forward in order, beginning with the next subobject after that described by the designator.149)
¶18 Each designator list begins its description with the current object associated with the closest surrounding brace pair. Each item in the designator list (in order) specifies a particular member of its current object and changes the current object for the next designator (if any) to be that member.150) The current object that results at the end of the designator list is the subobject to be initialized by the following initializer.
¶19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;151) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.
¶20 If the aggregate or union contains elements or members that are aggregates or unions, these rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the elements or members of the subaggregate or the contained union. Otherwise, only enough initializers from the list are taken to account for the elements or members of the subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next element or member of the aggregate of which the current subaggregate or contained union is a part.
¶21 If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
148) If the initializer list for a subaggregate or contained union does not begin with a left brace, its subobjects are initialized as usual, but the subaggregate or contained union does not become the current object: current objects are associated only with brace-enclosed initializer lists.
149) After a union member is initialized, the next object is not the next member of the union; instead, it is the next subobject of an object containing the union.
150) Thus, a designator can only specify a strict subobject of the aggregate or union that is associated with the surrounding brace pair. Note, too, that each separate designator list is independent.
151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.
Interpretation
I think your code is well-formed and that GCC is handling it incorrectly and Clang is handling it correctly.
With your code modified only so that the unused argc
and argv
are replaced by void
, running on a Mac with macOS Sierra 10.12.1, compiling with GCC 6.2.0 and with Apple's clang
version 'Apple LLVM version 8.0.0 (clang-800.0.42.1)', I get the same results as you:
0, 5, 0
from GCC.
1, 5, 3
from Clang.
The key wording in the standard is:
In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator.
In your initializer, you have:
struct foo f = { .bar = b, .bar.a = 5 };
The first part of the initializer, .bar = b,
clearly initializes the bar
subobject. At that point, .bar.b
has the value 1
, .bar.a
has the value 2
, .bar.r
has the value 3
. If you omit the , .bar.a = 5
portion of the initializer, the compilers agree.
When you include the , .bar.a = 5
, the designator causes the following initialize to begin intialization of the subobject described by the designator — and the designator is .bar.a
so the initialization 5
initializes .bar.a
. The compilers agree on this; both set .bar.a
to 5
. But the subobject designated by .bar
was previously initialized, so the initializer for .bar.a
only affects the .a
element; it should not override any other element.
If the initializer is extended with with , 19
, then the 19
is not a designation, but it initializes the subobject after the previous designation, which is .bar.r
. Both the compilers agree with this.
This test code, a minor variant on your code, illustrates:
#include <stdio.h>
struct bar
{
int b;
int a;
int r;
};
struct foo
{
struct bar bar;
};
static inline void foobar(struct foo f)
{
printf("%d, %d, %d\n", f.bar.b, f.bar.a, f.bar.r);
}
int main(void)
{
struct bar b = {1, 2, 3};
struct foo f0 = {.bar = b, .bar.a = 5 };
struct foo f1 = {.bar = b, .bar.a = 5, 19 };
struct foo f2 = {.bar = b };
foobar(f0);
foobar(f1);
foobar(f2);
return 0;
}
The output from GCC is:
0, 5, 0
0, 5, 19
1, 2, 3
The output from Clang is:
1, 5, 3
1, 5, 19
1, 2, 3
Note that even with no warnings specifically enabled, clang
gripes about this code:
$ clang -O3 -g -std=c11 so-4092-0714.c -o so-4092-0714
so-4092-0714.c:21:36: warning: subobject initialization overrides initialization of other fields within its
enclosing subobject [-Winitializer-overrides]
struct foo f0 = {.bar = b, .bar.a = 5 };
^~~~~~
so-4092-0714.c:21:29: note: previous initialization is here
struct foo f0 = {.bar = b, .bar.a = 5 };
^
so-4092-0714.c:22:36: warning: subobject initialization overrides initialization of other fields within its
enclosing subobject [-Winitializer-overrides]
struct foo f1 = {.bar = b, .bar.a = 5, 19 };
^~~~~~
so-4092-0714.c:22:29: note: previous initialization is here
struct foo f1 = {.bar = b, .bar.a = 5, 19 };
^
2 warnings generated.
$
As I said, I think Clang is initializing these structures correctly, even if it complains more than necessary while doing so.
struct
are not initialised. – Verniceclang -c -O3 -g -std=c11 -Wall -Wextra -Werror so-4092-0714.c
, you also get error messages such as:so-4092-0714.c:16:35: error: subobject initialization overrides initialization of other fields within its enclosing subobject [-Werror,-Winitializer-overrides]
pointing atstruct foo f = {.bar = b, .bar.a = 5 };
. So, clang (versionApple LLVM version 8.0.0 (clang-800.0.42.1)
) isn't entirely happy with the code. But it's only an error because I use-Werror
. – Stereophotographygcc --version
areApple LLVM version 8.0.0 (clang-800.0.42.1)
andgcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
– Doloritas