Определение загадочной структуры в C

Я наткнулся на следующий код определения лабиринта:

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) {}
        }
    }
};

Из связанного сообщения в блоге я понимаю, что они пытаются определить двоичное дерево с сыром на диаграмме.

Однако я не могу понять, что должен делать код C. Было бы здорово, если бы кто-то мог мне это объяснить.

2 ответа

Решение

В этом коде используется комбинация назначенных инициализаторов и составных литералов, которые оба являются функциями C99, я связал их с другим ответом, где я предоставляю стандартные кавычки для обеих этих функций.

Назначенные инициализаторы позволяют использовать указывать определенное поле для инициализации с помощью .fieldname = пример из связанного документа:

 struct point { int x, y; };

следующая инициализация

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

эквивалентно

struct point p = { xvalue, yvalue };

Другая используемая функция - составные литералы, которые используются для создания неназванных статических объектов, а затем код берет адрес этого объекта и присваивает ему соответствующие указатели. left а также right, Затем он использует эту функцию рекурсивно в пределах неназванных объектов, чтобы установить их left а также right указатели.

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

Эти безымянные объекты являются статическими, только если они используются вне тела функции, в противном случае они будут иметь автоматическую продолжительность хранения и прекратят свое существование после выхода из функции, поэтому принятие их адресов, как это делает код, вероятно, было бы неразумно.

Для справки я приведу стандартную цитату по составным литералам в своем ответе здесь.

Важно отметить, что при использовании назначенных инициализаторов любое поле, не инициализированное в явном виде, будет инициализировано в ноль, что на самом деле важно в этом случае, например hasCheese будет установлен в 0 если специально не установлено иначе.

Хотя это функции C99, не все компиляторы поддерживают или полностью поддерживают C99, мои тесты в Visual Studio показывают, что нам нужно заменить пустые составные литералы, например:

left = &(maze_t) {}

с NULL чтобы получить его для компиляции. Я подал отчет об ошибке.

Ответ на сообщение об ошибке был следующим, но в основном это расширение gcc / clang на работе:

Это расширение GNU. Clang поддерживает его как расширение (см. Опцию clang -Wgnu-empty-initializer).

Стандартный способ написать это - {0}, который будет обнулять все поля.

Код инициализирует структуру в соответствии с синтаксисом, разрешенным в C с 1999 года (C99 и C11).

Вкратце, вы можете инициализировать переменную структуры, записав только "члены" структуры, заключенные в фигурные скобки { }.

Например, с учетом следующей структуры:

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

мы можем определить и инициализировать переменные структуры следующим образом:

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

Как видите, достаточно написать члены без имени переменной r,
Этот синтаксис разрешен в инициализаторах.

Кроме того, в обычных присваиваниях мы можем иметь подобный синтаксис с помощью составных литералов, как в этом примере:

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

Поищите в Интернете следующие темы: инициализаторы C struct и составные литералы C, чтобы получить больше информации (техническое примечание: ANSI C89 не имеет этого синтаксиса, поэтому ищите ISO C99 и ISO C11).

Другие вопросы по тегам