Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cryptic struct definition in C

I came across the following maze definition code:

typedef struct mazeNode {
    int hasCheese;
    int tag;
    struct mazeNode *left;
    struct mazeNode *right;
} maze_t;

maze_t maze = {
    .tag = 1,
    .left = &(maze_t) {
        .left = &(maze_t) {
            .left = &(maze_t) {},
            .right = &(maze_t) {}
        },
        .right = &(maze_t) {
            .right = &(maze_t) {}
        }
    },
    .right = &(maze_t) {
        .tag = 8,
        .left = &(maze_t) {},
        .right = &(maze_t) {
            .tag = 10,
            .left = &(maze_t) {
                .tag = 11,
                .left = &(maze_t) {
                    .hasCheese = 1,
                    .tag = 12
                }
            },
            .right = &(maze_t) {}
        }
    }
};

From the linked blog post I understand that they are trying to define the binary tree with the cheese in the diagram.

However I can't seem to make head or tail out of what the C code is supposed to do. It would be great if someone could explain it to me.

like image 337
liv2hak Avatar asked Aug 30 '14 01:08

liv2hak


2 Answers

This code is using a combination of designated initializers and compound literals, which are both C99 features, I linked to other answer where I provide standard quotes for both of these features.

Designated initializers allow you to use specify a specific field to initialize using .fieldname =, an example from the linked document is:

 struct point { int x, y; };

the following initialization

struct point p = { .y = yvalue, .x = xvalue };

is equivalent to

struct point p = { xvalue, yvalue };

The other feature being used is compound literals which is being used to create unnamed static objects and then the code takes the address of this object and assigns it the respective pointers left and right. It then uses this feature recursively within the unnamed objects to set their respective left and right pointers.

 .left = & (maze_t) { .... }
           ^^^^^^^^^^^^^^^^
           unnamed static object

These unnamed objects are only static if they are used outside the body of a function otherwise they will have automatic storage duration and would cease to exist once you exit the function and so taking their addresses like the code is doing would probably be unwise.

For reference I provide a standard quote on compound literals in my answer here.

Important to note that when using designated initializers any field not explicitly initialized will be initialized to zero, which is actually important in this case, for example hasCheese will be set to 0 unless it is specifically set otherwise.

Although these are C99 features not all compilers support or fully support C99, my tests on Visual Studio show that we need to replace the empty compound literals, for example:

left = &(maze_t) {}

with a NULL to get it to compile. I filed a bug report.

The response to the bug report was as follows, but basically this is a gcc/clang extension at work:

This is a GNU extension. Clang supports it as an extension (see the clang option -Wgnu-empty-initializer).

The standard way to write this is {0}, which will zero initialize all fields.

like image 74
Shafik Yaghmour Avatar answered Nov 03 '22 10:11

Shafik Yaghmour


The code is initializing a struct according to a syntax allowed in the C since 1999 (C99 and C11).

Briefly explained, you can initialize a struct variable by writting only the "members" of the struct enclosed in braces { }.

For example, given the following struct:

 struct fractional_number_s { int numerator; unsigned int denominator; };

we can define and initialize struct variables as follows:

 struct fractional_number_s r = { .numerator = 3, .denominator = 7, };  

As you can see, it's enough to write the members, without the variable-name r.
This syntax is allowed in initializers.

Also, in normal assignments we can have a similar syntax with the aid of compound literals, as in this example:

 r = (struct fractional_numbers_s) { .numerator = 3, .denominator = 7 };  

Search on internet about these topics: C struct initializers and C compound literals, to obtain more information (technical note: ANSI C89 doesn't have this syntax, so search for ISO C99 and ISO C11).

like image 3
pablo1977 Avatar answered Nov 03 '22 11:11

pablo1977