Reputation: 281
My professor’s final exam mostly consisted of very tricky syntax. For example, some of his questions were like "use pointer 1 to print out the letter k without using brackets. Luckily it was open book.
So one question was:
int a[2][2][2] = {{5,6}, {7,8}, {9,10}, {11,12}};
write a printf statement that will print out "7910". Using pointers without using square brackets.
At first, I thought this was a typo or an illegal array. I thought the array is supposed to stop at the third array from the left.
I wrote:
printf("%d%d%d\n",*(*(a+1)+1)),*(*(a+2)),*(*(a+2)));
I put this because if the array was
int a[2][2] = {{7,8},{11,12}};
a syntax similar would work.
So was this a typo? If not, what is the right syntax?
Upvotes: 7
Views: 151
Reputation: 37914
This line:
int a[2][2][2] = {{5,6}, {7,8}, {9,10}, {11,12}};
is not valid C and a compiler should report a diagnostic message.
According to C11 N1570, §6.7.9/20 Initialization:
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.
The problem is that {{5,6}, {7,8}, {9,10}, {11,12}}
attempts to initialize four elements of the most outer array object (i.e. a
represents array of array array of int
), while its size has been explicitely stated as two:
int a[2][2][2] = {{5,6}, {7,8}, {9,10}, {11,12}};
| | | | |
| | | | |
-----------------------------------
This is constraint violation, as by subclause §6.7.9/2:
No initializer shall attempt to provide a value for an object not contained within the entity being initialized.
It would be valid if you omit its first size, as shown below:
int a[][2][2] = {{5,6}, {7,8}, {9,10}, {11,12}};
which is effectively the same as:
int a[4][2][2] = {
{
{5, 6},
{0, 0}
},
{
{7, 8},
{0, 0}
},
{
{9, 10},
{0, 0}
},
{
{11, 12},
{0, 0}
}
};
Note that you may initialize it as:
int a[2][2][2] = {5, 6, 7, 8, 9, 10, 11, 12}; // legal, but not advisable
or even:
int a[2][2][2] = {{5, 6, 7, 8}, {9, 10, 11, 12}}; // bad style
This is allowed by remaining sentence of the mentioned §6.7.9/20:
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.
Upvotes: 1
Reputation: 106012
Compiler, in most cases, will parse the above initialization as
int a[2][2][2] = { { {5,6}, {7,8} },
{ {9,10}, {11,12} }
};
This will also work if you will write it as
int a[2][2][2] = {5, 6, 7, 8, 9, 10, 11, 12};
but this is really not a good practice.
C standard says about it
§6.7.9(p17):
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 union149). [...]
p26
EXAMPLE 3 The declaration
int y[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
is a definition with a fully bracketed initialization:
1
,3
, and5
initialize the first row ofy
(the array objecty[0]
), namelyy[0][0]
,y[0][1]
, andy[0][2]
. Likewise the next two lines initializey[1]
andy[2]
. The initializer ends early, soy[3]
is initialized with zeros. Precisely the same effect could have been achieved byint y[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
The initializer for
y[0]
does not begin with a left brace, so three items from the list are used. Likewise the next three are taken successively fory[1]
andy[2]
.
Upvotes: 5