2010-07-12 11 views
37

aquí es muy simplificado código de problema que tengo:Unión anónima dentro de struct no en 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; 
} 

Y lo que no undestand es la siguiente:

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

Usando GCC sin -std opción compila el código de seguridad sin ningún problema (y el código similar está funcionando bastante bien), pero parece que c99 no permite esta técnica. ¿Por qué es así y es posible hacer es c99 (o c89, c90) compatible? Gracias.

+1

Solo una nota, clang compila el código dado con y sin '-std = c99' en silencio, sin ningún error ni advertencia. – Martin

Respuesta

2

Unión debe tener un nombre y ser declarados como esto:

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

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

No en C11, pero en C99 sí. Pero dado que hemos pasado la marca de los tres años desde su lanzamiento, tal vez es hora de comenzar a pasar -std = c11 :). –

50

sindicatos anónimos son una extensión de GNU, no forma parte de ninguna versión estándar del lenguaje C. Puede utilizar -std = gnu99 o algo por el estilo para las extensiones de C99 + GNU, pero lo mejor es escribir C adecuada y no depender de las extensiones que proporcionan más que el azúcar sintáctica ... Editar

: se añadieron sindicatos anónimos en C11, por lo que ahora son una parte estándar del lenguaje. Es de suponer que el -std=c11 de GCC te permite usarlos.

4

Bueno, la solución fue nombrar la instancia de la unión (que puede permanecer anónima como tipo de datos) y luego usar ese nombre como proxy.

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

Ahora se compila como c99 sin ningún problema.

 
$ cc -std=c99 us.c 
$ 

Nota: No estoy feliz por esta solución de todos modos.

+3

¡Deberías estar feliz! Es la manera estándar de acceder a los miembros del sindicato, garantizados para trabajar con cualquier compilador de C desde el 1 de enero de 1970. – Jens

+2

Me da algo de feo el código, ni idea de por qué no estaba incluido en K & R C, me parece una característica simple y útil ... De todos modos, uso el mismo método proxy pero defino macros para evitar todo el tipeo. –

+2

Me doy cuenta de que esta es una publicación muy antigua, pero copiar el código real en lugar de un parche diff es mucho más legible. – ysap

0

En cuanto a 6.2.7.1 de C99, estoy viendo que el identificador es opcional:

  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 

He estado arriba y búsqueda hacia abajo, y no se puede encontrar ninguna referencia a las uniones anónimas estar en contra del especulación. El sufijo -opt completo indica que la cosa, en este caso identifier es opcional de acuerdo con 6.1.

+4

Creo que hay un malentendido aquí. El identificador para una etiqueta struct o union ** es opcional, pero no el identificador que se está declarando. No puede decir 'union {...};' dentro de un agregado por el mismo motivo por el que no puede decir 'int;'. En el caso de unión, las extensiones del compilador lo permiten, porque puede usar identificadores en la parte '{...}' cuando usa una unión anónima. – Jens

21

Estoy encontrando esta pregunta aproximadamente un año y medio después que todos los demás, por lo que puedo dar una respuesta diferente: las estructuras anónimas no están en el estándar C99, pero están en el estándar C11. GCC y clang ya lo admiten (el estándar C11 parece haber eliminado la función de Microsoft, y GCC ha brindado soporte para algunas extensiones de MSFT durante algún tiempo).

1

Otra solución es poner el valor de encabezado común (enum node_type type) en cada estructura y hacer que su estructura de nivel superior sea una unión. No es exactamente "Do not Repeat Yourself", pero evita las uniones anónimas y los valores de proxy incómodos.

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

Para lectores tardíos como yo: DRY podría evitarse mediante el uso de una plantilla y typedefs apropiados: 'template struct base_node {/*[...]*/ T value;}; typedef base_node int_node; ' – Aconcagua

+3

En C++ tal vez, pero no en C99. – theJPster

+0

Ah, perdón, de alguna manera se olvidó de estar en C mientras leía y pensaba ... En última instancia, he estado demasiado metido en C++. – Aconcagua

Cuestiones relacionadas