Определение загадочной структуры в 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).