The standard says ...
I will quote from §6.7.9 the Initializers ISO / IEC 9899: 2011 (standard C11), in the same section that Vlad from Moscow quotes in his answer:
¶16 Otherwise, the initializer for an object that is of type aggregate or union must be a list of initializers for elements or named elements enclosed in brackets.
¶17 Each initializer list enclosed in brackets has a linked current object. When there is no designation, the subobjects of the current object are initialized in order according to the type of the current object: array elements in ascending substring order, structure members in the declaration order, and the first named member of the union. 148) On the contrary, the designation causes the next initializer to start initializing the subobject described by the pointer. Initialization then continues in order, starting with the next subobject after it is described by the symbol. 149)
¶18 Each symbol list starts its description with the current object associated with the nearest neighboring bracket. Each element in the notation list (in order) indicates a specific member of its current object and changes the current object for the next pointer (if any) as that member. 150) The current object that leads to the end of the notation list is a subobject that must be initialized by the next initializer.
¶19 Initialization must be performed in the order of the list of initializers, each initializer has provided for a specific subobject redefining any previously specified initializer for the same subobject; 151) all subobjects that are not explicitly initialized should be implicitly initialized the same as objects that have a static storage duration.
¶20 If an aggregate or association contains elements or elements that are aggregates or associations, these rules apply recursively to subgroups or combined associations. If the initializer of a subaggregate or contained union begins with a left bracket, the initializers enclosed in this bracket and its matching right bracket initialize the elements or elements of the subaggregate or combined union. Otherwise, only a sufficient number of initializers from the list are taken into account for elements or members of the sub-aggregate or the first member of the joint union; any remaining initializers are left to initialize the next element or member of the population, of which the current sub-aggregate or containing the union is a part.
¶21 If the list enclosed in curly brackets contains fewer initializers than the element or elements of the collection or fewer characters in the string literal used to initialize an array of known size than in the array, the rest of the collection must be initialized implicitly in the same way as objects having a static storage duration.
148) If the list of initializers for the combined or contained union does not start with the left bracket, its subobjects are initialized as usual, but the sub-aggregate or containing the combined does not become the current object: the current objects are associated only with lists of initializers enclosed in brackets.
149) After the member of the association is initialized, the next object is not the next member of the association; instead, it is the next subobject of the object containing the union.
150) Thus, a pointer can indicate only a strict subobject of a population or association associated with a neighboring brace. Also note that each individual list of notation is independent.
151) Any initializer for a subobject that is overridden and therefore not used to initialize this subobject may not be evaluated at all.
Interpretation
I think your code is well-formed and that GCC doesn’t handle it correctly, and Clang processes it correctly.
With the modified code only so that unused argc and argv are replaced with void , working on Mac with macOS Sierra 10.12.1, compilation with GCC 6.2.0 and with Apple clang version "Apple LLVM version 8.0.0 (clang-800.0. 42.1) ", I get the same results as you:
0, 5, 0 from the GCC.1, 5, 3 from Clang.
Key wording in the standard:
On the contrary, the notation causes the next initializer to start initializing the subobject described by the pointer.
In your initializer, you have:
struct foo f = { .bar = b, .bar.a = 5 };
The first part of the initializer .bar = b, explicitly initializes the subobject bar . At this point .bar.b is 1 , .bar.a is 2 , .bar.r is 3 . If you omit the part , .bar.a = 5 initializer, the compilers agree.
When you turn , .bar.a = 5 , the pointer calls the next initialization to start initializing the subobject described by the pointer - and the designation .bar.a , so initializing 5 initializes .bar.a . Compilers agree with this; both set .bar.a to 5 . But the subobject indicated by .bar was previously initialized, so the initializer for .bar.a only affects the .a element; it should not redefine any other element.
If the initializer is expanded with , 19 , then 19 not a designation, but it initializes the subobject after the previous designation, which is .bar.r . Both compilers agree with this.
This test code, a minor version of your code, illustrates:
Exiting GCC:
0, 5, 0 0, 5, 19 1, 2, 3
Exiting Clang:
1, 5, 3 1, 5, 19 1, 2, 3
Note that even without special warnings, clang captures 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 Klang correctly initializes these structures, even if he complains more than necessary by doing this.