Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are these three dimensional arrays legal in C?

Tags:

arrays

c

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?

like image 508
SaturnsBelt Avatar asked Jul 27 '17 19:07

SaturnsBelt


People also ask

Is 3D array possible in C?

You can think the array as a table with 3 rows and each row has 4 columns. Similarly, you can declare a three-dimensional (3d) array. For example, float y[2][4][3];

Does C allow multi-dimensional array?

We can declare a two-dimensional integer array say 'x' of size 10,20 as: int x[10][20]; Elements in two-dimensional arrays are commonly referred to by x[i][j] where i is the row number and 'j' is the column number.

Why do we use 3 dimensional arrays?

A 3D array provides range, azimuth and elevation information and represents a maximum complexity design. As the 2D array provides range and azimuth information only, it represents a medium complexity design. The arrays can be used for radar applications such as air-traffic control and surveillance.


2 Answers

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, and 5 initialize the first row of y (the array object y[0]), namely y[0][0], y[0][1], and y[0][2]. Likewise the next two lines initialize y[1] and y[2]. The initializer ends early, so y[3] is initialized with zeros. Precisely the same effect could have been achieved by

int 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 for y[1] and y[2].

like image 164
haccks Avatar answered Oct 04 '22 04:10

haccks


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.

like image 44
Grzegorz Szpetkowski Avatar answered Oct 04 '22 02:10

Grzegorz Szpetkowski