Cryptic struct definition in C
Asked Answered
T

2

10

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.

Tema answered 30/8, 2014 at 1:17 Comment(3)
The code presented is accompanied by a blog post in which the author describes the code.Occlusive
They are using compound literals.Univalence
The .left = &(maze_t){}; and .right = &(maze_t){}; would more naturally be initialized to 0 or NULL. As it stands, there is an extra element in the hierarchy with a .tag value of 0 and a .hasCheese value of 0 too (and null left and right pointers).Dryclean
U
10

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.

Univalence answered 30/8, 2014 at 1:43 Comment(5)
is this standard feature across all platforms.Or does using such features affect portabiliy?Tema
@Tema They are both C99 features and so both gcc and clang support them. Visual Studio recently started supporting C99 features but I am not totally sure how well supported these features are.Univalence
@Tema ok, Visual Studio does not like the code as is, so not fully portable.Univalence
Thought I should make a note here that this is not a bug in Visual Studio as empty compound literals are actually a GNU extension which you can see with -pedantic enabled warning: use of GNU empty initializer extension [-Wgnu-empty-initializer]. The standard way to initialise all fields to zero is {0}.Rousing
@Rousing thank you, I thought I updated this question when I received the bug report response. Clearly I did not, let me do that now.Univalence
N
3

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

Neocolonialism answered 30/8, 2014 at 1:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.