вот очень упрощенный код проблемы, который у меня есть:
enum node_type {t_int, t_double}; struct int_node {int value; }; struct double_node {double value; }; struct node {enum node_type type; union {struct int_node int_n; struct double_node double_n; }; }; int main (void) {struct int_node i; i.value = 10; struct node n; n.type = t_int; п. int_n = i; return 0; }
И я не расстроен:
$ cc us.c $ cc -std = c99 us.c us.c: 18: 4: предупреждение: декларация ничего не объявляет us.c: В функции «main»: us.c: 26: 4: error: «struct node» не имеет имени с именем 'int_n'
Использование GCC
без опции -std
компилирует код выше без каких-либо проблем (и аналогичный код работает очень хорошо), но кажется, что c99
не разрешает эту технику. Почему это так, и возможно ли сделать совместимость c99
(или c89
, c90
)? Благодарю.
Анонимные союзы – это расширение GNU, а не часть стандартной версии языка C. Вы можете использовать -std = gnu99 или что-то подобное для расширений c99 + GNU, но лучше всего писать C и не полагаться на расширения, которые не содержат ничего, кроме синтаксического сахара …
Изменить: Анонимные союзы были добавлены в C11, поэтому теперь они являются стандартной частью языка. Предположительно GCC’s -std=c11
позволяет использовать их.
Я нахожу этот вопрос примерно через полтора года после того, как все это сделали, поэтому я могу дать другой ответ: анонимные структуры не входят в стандарт C99, но они соответствуют стандарту C11. GCC и clang уже поддерживают это (стандарт C11, похоже, отменил эту функцию от Microsoft, и GCC некоторое время поддерживал некоторые расширения MSFT).
Ну, решение было назвать экземпляр объединения (который может оставаться анонимным как тип данных), а затем использовать это имя в качестве прокси.
$ diff -u old_us.c us.c --- old_us.c 2010-07-12 13: 49: 25.000000000 +0200 +++ us.c 2010-07-12 13: 49: 02.000000000 +0200 @@ -15,7 +15,7 @@ объединение { struct int_node int_n; struct double_node double_n; -}; +} данные; }; int main (void) { @@ -23,6 +23,6 @@ i.value = 10; struct node n; n.type = t_int; - n.int_n = i; + n.data.int_n = i; return 0; }
Теперь он компилируется как c99
без каких-либо проблем.
$ cc -std = c99 us.c $
Примечание. В любом случае, я не очень рад этому решению.
Союз должен иметь имя и быть объявлен следующим образом:
union UPair { struct int_node int_n; struct double_node double_n; }; UPair X; X.int_n.value = 12;
Другое решение состоит в том, чтобы поместить общее значение заголовка ( enum node_type type
) в каждую структуру и сделать структуру верхнего уровня объединением. Это не совсем «Do not Repeat Yourself», но он избегает как анонимных союзов, так и неудобно выглядящих значений прокси.
enum node_type { t_int, t_double }; struct int_node { enum node_type type; int value; }; struct double_node { enum node_type type; double value; }; union node { enum node_type type; struct int_node int_n; struct double_node double_n; }; int main(void) { union node n; n.type = t_int; // or n.int_n.type = t_int; n.int_n.value = 10; return 0; }
Взглянув на 6.2.7.1 из C99, я вижу, что идентификатор является необязательным:
struct-or-union-specifier: struct-or-union identifier-opt { struct-declaration-list } struct-or-union identifier struct-or-union: struct union struct-declaration-list: struct-declaration struct-declaration-list struct-declaration struct-declaration: specifier-qualifier-list struct-declarator-list ; specifier-qualifier-list: type-specifier specifier-qualifier-list-opt type-qualifier specifier-qualifier-list-opt
Я искал поиск и не мог найти ссылки на анонимные профсоюзы, которые были против спецификации. Весь суффикс -opt указывает, что вещь, в этом случае identifier
является необязательным в соответствии с 6.1.