1

I think I get how padding works with a proper format, i.e. this:

char arr[3][2] = {{1}, {4,5}};

is equivalent to

char arr[3][2] = {1, 0, 4, 5, 0, 0};

And, for fun, I decided to throw bad formats at GCC to see what it returns. For the following code: char arr[3][3] = {{1,2,3},12,{4,5,6}};, GCC returns:

{1, 2, 3, 12, 4, 0, 0, 0, 0}

but I guessed it would return:

{1, 2, 3, 12, 4, 5, 6, 0, 0}

It seems the 12 throws it off causing "excess elements in scalar initializer" warnings for {4,5,6}, and hence the 5 and 6 are dropped.

What's the algorithm used to flatten(for lack of a better term) these multi-dimensional array initializations?

Here's what GCC says:

test.c: In function 'main':
test.c:119:2: warning: braces around scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
  ^~~~
test.c:119:2: note: (near initialization for 'arr[1][1]')
test.c:119:36: warning: excess elements in scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
                                  ^
test.c:119:36: note: (near initialization for 'arr[1][1]')
test.c:119:38: warning: excess elements in scalar initializer
  char arr[3][3] = {{1,2,3},12,{4,5,6}};
                                    ^
test.c:119:38: note: (near initialization for 'arr[1][1]')
6
  • 2
    I think what happens is this. Once you started eliding braces, you can't go back to using them again. So {1, 2, 3} initializes arr[0]. Then 12 is an initializer for arr[1][0], and {4, 5, 6} is an initializer for arr[1][1]. You get the same warning from this as you would from int x = {4, 5, 6}; Commented Jul 27, 2020 at 13:45
  • 1
    @EugeneSh.: {{1,2,3},12,{4,5,6}} does not have any types except the int constants. Lists of initializers do not form types. Commented Jul 27, 2020 at 13:45
  • 2
    I have discussed how brace-enclosed lists in initializers are processed previously. Commented Jul 27, 2020 at 13:47
  • @IgorTandetnik Interesting! So could that be why we see a near initialization for 'arr[1][1]' note? Commented Jul 27, 2020 at 13:49
  • 1
    It goes sub-aggregate by sub-aggregate, each three-element sub-array separately. I guess more accurate statement would be that you can start using braces again, but it has to be precisely on a sub-aggregate boundary. Commented Jul 27, 2020 at 14:01

1 Answer 1

3

C 2018 6.7.9 paragraphs 17 to 21 discuss how aggregates are initialized from brace-enclosed lists. 17 says:

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. 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.

20 tells us about contained arrays:

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 tells us about missing initializers:

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.

Now we can see how char arr[3][3] = {{1,2,3},12,{4,5,6}}; is processed.

First, we are initializing arr, so, by paragraph 17, we will initialize its three elements, arr[0], arr[1], and arr[2], in order. Since the first item in the list {1,2,3}, 12, {4,5,6} is a brace-enclosed list, paragraph 20 tells us that the items in that list, {1,2,3} will be used to initialize the subaggregate arr[0].

So arr[0][0], arr[0][1], and arr[0][2] are initialized to 1, 2, and 3.

Next, we consider arr[1]. The initializer for arr[1] starts with 12, not with a left brace, so we are not taking initializers from a brace-enclosed list. So we have two items left in the list, 12 and {4,5,6}, to use for initializing arr[1][0], arr[1][1], and arr[1][2] (and, later, the elements of arr[2]).

So 12 initializes arr[1][0], producing 12, of course. Then {4,5,6} initializes arr[1][1].

There are two problems here. One is that the rules in paragraph 20 says that if we are initializing a subaggregate with a brace-enclosed list, the initializers in the list are used for the elements or members of the subaggregate. But we are attempting to initialize arr[1][1], and it is a scalar object, not an aggregate. So the rules do not tell us what to do. I believe the behavior is not defined.

Further, if we do take this brace-enclosed list to be supplying initial values, it has too many values, and this may also be undefined because paragraph 2 says:

No initializer shall attempt to provide a value for an object not contained within the entity being initialized.

Regardless of the lack of definition, it appears GCC has taken the first item of the brace-enclosed list to initialize arr[1][1] and discarded the rest. This seems reasonable. In contrast, taking the 5 and 6 to initialize other array elements seems less reasonable: Due to the processing specified by the C standard, the entire brace-enclosed list has been associated with arr[1][1], and there is no reason to take values from that list to use for other array elements.

Assuming the compiler continues after the undefined behavior, there are no initializers for arr[1][2], and the elements of arr[2]. So the rule in paragraph 21 about missing initializers applies: They are initialized as if for a static object, which, for a char, means they are initialized to zero.

Sign up to request clarification or add additional context in comments.

1 Comment

I would say initialising {{1, 2, 3}, 12, {4, 5, 6}} has the same effect as initialising {{1, 2, 3}, {12, {4, 5, 6}}}, hasn't it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.