2010-07-12 2 views
37

здесь очень упрощена код проблемы у меня есть:Анонимный союз внутри структуры не в 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; 
} 

И то, что я не undestand это:

 
$ 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) Совместим? Благодарю.

+1

Только примечание, clang компилирует заданный код с и без '-std = c99' без каких-либо ошибок и предупреждений. – Martin

ответ

2

Союз должен иметь название и быть объявлен как это:

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

UPair X; 
X.int_n.value = 12; 
+2

Не в C11, а в C99 да. Но так как мы прошли трехлетний знак с момента его выпуска, возможно, пришло время начать передачу -std = c11 :). –

50

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

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

4

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

 
$ 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 
$ 

Примечание: Я не в восторге от этого решения в любом случае.

+3

Вы должны быть счастливы! Это стандартный способ доступа к членам профсоюза, гарантированный для работы с любым компилятором C с 1 января 1970 года. – Jens

+2

Это немного увеличивает код, не знаю, почему он не был включен в K & R C, кажется мне простой и полезной функцией ... Во всяком случае, я использую один и тот же метод прокси, но определяю макросы, чтобы избежать ввода текста. –

+2

Я понимаю, что это очень старое сообщение, но копирование фактического кода вместо патча diff намного читаемо. – ysap

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.

+4

Я думаю, что здесь есть недоразумение. Идентификатор тэга struct или union ** является необязательным, но не объявляемым идентификатором. Вы не можете сказать 'union {...};' в некоторой совокупности по той же причине вы не можете сказать 'int;'. В случае объединения расширения для компилятора допускают это, потому что вы можете использовать идентификаторы в '{...}' при использовании анонимного объединения. – Jens

21

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

1

Другое решение состоит в том, чтобы поместить общее значение заголовка (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; 
} 
+0

Для таких поздних читателей, как я: DRY можно было бы избежать, используя шаблон и соответствующие typedefs: 'template struct base_node {/*[...]*/ T value;}; typedef base_node int_node; ' – Aconcagua

+3

В C++ возможно, но не на C99. – theJPster

+0

Ах, извините, как-то забыл о том, чтобы быть в C, читая и думая ... Я был слишком глубоко в C++ в конечном счете. – Aconcagua

Смежные вопросы