Подтвердить что ты не робот

Анонимный союз внутри структуры не в c99?

здесь очень упрощенный код проблемы, который у меня есть:

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;
    n.int_n = i;
    return 0;
}

И я не знаю, что это:

$ cc us.c 
$ cc -std=c99 us.c 
us.c:18:4: warning: declaration does not declare anything
us.c: In function ‘main’:
us.c:26:4: error: ‘struct node’ has no member named ‘int_n’

Использование GCC без -std опции компилирует код выше без проблем (и аналогичный код работает очень хорошо), но кажется, что c99 не разрешает эту технику. Почему это так, и возможно ли сделать c99 (или c89, c90) совместимым? Спасибо.

4b9b3361

Ответ 1

Анонимные объединения - это расширение GNU, а не часть стандартной версии языка C. Вы можете использовать -std = gnu99 или что-то подобное для расширений c99 + GNU, но лучше всего писать C и не полагаться на расширения, которые не содержат ничего, кроме синтаксического сахара...

Изменить: Анонимные союзы были добавлены в C11, поэтому теперь они являются стандартной частью языка. Предположительно GCC -std=c11 позволяет использовать их.

Ответ 2

Я нахожу этот вопрос примерно через полтора года после того, как все сделали, поэтому я могу дать другой ответ: анонимные структуры не входят в стандарт C99, но они соответствуют стандарту C11. GCC и clang уже поддерживают это (стандарт C11, похоже, снял эту функцию у Microsoft, и GCC некоторое время поддерживал некоторые расширения MSFT).

Ответ 3

Ну, решение состояло в том, чтобы назвать экземпляр объединения (который может оставаться анонимным как тип данных), а затем использовать это имя в качестве прокси.

$ 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 @@
   union {
     struct int_node int_n;
     struct double_node double_n;
-  };
+  } data;
 };

 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 
$ 

Примечание: я все равно не рад этому решению.

Ответ 4

Союз должен иметь имя и быть объявлен следующим образом:

union UPair {
    struct int_node int_n;
    struct double_node double_n;
};

UPair X;
X.int_n.value = 12;

Ответ 5

Глядя на 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.

Ответ 6

Другое решение состоит в том, чтобы поместить общее значение заголовка (enum node_type type) в каждую структуру и сделать структуру верхнего уровня объединением. Это не совсем "Не повторяй себя", но он избегает как анонимных союзов, так и неудобно выглядящих значений прокси.

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

Ответ 7

Просто для разъяснений по поводу анонимной struct или анонимного union.

С17/18

6.7.2.1 Структура и объединение спецификаторов

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

C11

6.7.2.1 Структура и объединение спецификаторов

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

C99 Нет анонимной структуры или объединения

Упрощено: Идентификатор типа-идентификатора { тэг объявления-списка } ;

  • Спецификатор типа: struct или union;
  • Идентификатор: необязательно, ваше собственное имя для struct или union;
  • Список объявлений: члены, ваши переменные, анонимная struct и анонимный union
  • Метки: необязательно. Если у вас есть typedef перед спецификатором типа, теги являются псевдонимами, а не тегами.

Это анонимная struct или анонимное union только если оно не имеет идентификатора и тега и существует внутри другой struct или union.

struct s {
    struct { int x; };     // Anonymous struct, no identifier and no tag
    struct a { int x; };   // NOT Anonymous struct, has an identifier 'a'
    struct { int x; } b;   // NOT Anonymous struct, has a tag 'b'
    struct c { int x; } C; // NOT Anonymous struct
};

struct s {
    union { int x; };     // Anonymous union, no identifier and no tag
    union a { int x; };   // NOT Anonymous union, has an identifier 'a'
    union { int x; } b;   // NOT Anonymous union, has a tag 'b'
    union c { int x; } C; // NOT Anonymous union
};

typedef hell: если у вас есть typedef часть тега больше не является тегом, это псевдоним для этого типа.

struct a { int x; } A; // 'A' is a tag
union a { int x; } A;  // 'A' is a tag

// But if you use this way
typedef struct b { int x; } B; // 'B' is NOT a tag. It is an alias to struct 'b'
typedef union b { int x; } B;  // 'B' is NOT a tag. It is an alias to union 'b'

// Usage
A.x = 10; // A tag you can use without having to declare a new variable

B.x = 10; // Does not work

B bb; // Because 'B' is an alias, you have to declare a new variable
bb.x = 10;

Пример ниже просто измените struct для union, работайте так же.

struct a { int x; }; // Regular complete struct type
typedef struct a aa; // Alias 'aa' for the struct 'a'

struct { int x; } b; // Tag 'b'
typedef struct b bb; // Compile, but unusable.

struct c { int x; } C; // identifier or struct name 'c' and tag 'C'
typedef struct { int x; } d; // Alias 'd'
typedef struct e { int x; } ee; // struct 'e' and alias 'ee'