Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I not need 3 levels of braces for initializing 3 levels of arrays?

I came across to this example

struct sct
{
    int t[2];
};

struct str
{
    sct t[2];
};

int main()
{
    str t[2] = { {0, 2, 4, 6}, {1, 3, 5, 7} }; //Who does this work?

    cout << t[1].t[0].t[1] << t[0].t[1].t[0];     

    return 0;
}

This compiles and runs fine. It gives the output 34

I expected the syntax for initialization would be:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

Instead of

 { {0, 2, 4, 6}, {1, 3, 5, 7} };

But this gave:

In function 'int main()':
error: too many initializers for 'str'

Can someone explain why?

This is a picture to illustrate the way I see this: enter image description here

How should i think when it comes to initializing nested structs?

like image 800
Cătălina Sîrbu Avatar asked Feb 08 '20 08:02

Cătălina Sîrbu


People also ask

What are the different ways of initializing array?

There are two ways to specify initializers for arrays: With C89-style initializers, array elements must be initialized in subscript order. Using designated initializers, which allow you to specify the values of the subscript elements to be initialized, array elements can be initialized in any order.

Which of the following is correct initialization of an array?

The correct answer is: ​num[4] = {1,2,3,4}; Concept: The array is a data structure used to store elements in linear order. The array is declared to store elements of a particular data type.

What is the purpose of initializing an array?

Array initialization is the process of assigning/storing elements to an array. The initialization can be done in a single statement or one by one. Note that the first element in an array is stored at index 0, while the last element is stored at index n-1, where n is the total number of elements in the array.

What happens if an array element is not initialized?

If the array is not initialized at the time of declaration or any time after that then it will contain some random values in each memory position.


1 Answers

This looks like a simple typo but the situation is complex enough to tackle it step by step.

First let me show the solution that seems to work:

int main()
{
    str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };
    cout << t[1].t[0].t[1] << t[0].t[1].t[0] << endl;

    return 0;
}

So we have an array of str that holds an array of sct.

Let's start with the latter. You initialize an array of sct with something like this:

sct x[2] = { {0, 1}, {2, 3} };

Now for a single instance of str you could go with

str y = { { {0, 2}, {4, 6} } };

What remains for str t[2] is to arrange two copies of str initializing expressions inside curly brackets:

str t[2] = { { { {0, 2}, {4, 6} } }, { { {1, 3}, {5, 7} } } };

Edited: On the first reading I misunderstood the question. After the post was updated it became clear that the question is on why it is possible to shed two pair of braces but shedding just one pair results in a syntax error.

To understand how the parser is interpreting the code you might like to look at the parse tree. You can make gcc dump trees at several stages of the parser with -fdump-tree-... options. Here -fdump-tree-original may be useful.

To avoid additional confusion let's make sure the elements of the structures have different names:

struct sct
{
    int a[2];
};

struct str
{
    sct b[2];
};

Here is the output I got with GCC 7.5 from

>>>> CODE:
str t[2] = { { 0, 2, 4, 6 }, { 1, 3, 5, 7 } };
>>>> tree enabled by -tree-original
struct str t[2] = {{.b={{.a={0, 2}}, {.a={4, 6}}}}, {.b={{.a={1, 3}}, {.a={5, 7}}}}};

You can see that the compiler adds implicit brackets around the initializing expressions for each structure and around the initializing expressions for each named field.

Now consider the expression that fails to compile:

str t[2] = {  { {0, 2},{4, 6} }, { {1, 3},{5, 7} }   };

A the upper level the tree for this expression would be

/*Step 1: */ struct str t[2] = { {.b={0, 2}, {4, 6} }, {.b={1, 3}, {5, 7} } };

But as b is an array of sct, we try to initialize that with {0,2} getting

sct b[2] = {0, 2};

This expands to

struct sct b[2] = {{.a={0, 2} }};

This is valid C++ since the first element of the array is initialized explicitly and the second element is initialized implicitly with zeros.

With this knowledge we get the following tree

/*Step 2: */ struct str t[2] = { {.b={{.a={0, 2} }}, {4, 6} }, {.b={{.a={1, 3} }}, {5, 7} } };

Now we are left with the following:

 struct str z = { { { {0,2} }, { {0,0} } }, {4, 6} };

And the compiler rightfully complains:

 error: too many initializers for ‘str’

As a final check consider the following declaration

 struct sxc
 {
     sct b[2];
     int c[2];
 }

 struct sxc z = { {0,2} , {4, 6} };

This compiles and result in the following structure:

 { .b = { { .a={0,2} }, { .a={0,0} } }, .c={4, 6} }
like image 174
Dmitri Chubarov Avatar answered Oct 08 '22 02:10

Dmitri Chubarov