voici le code de problème très simplifié que j'ai:
enum node_type { t_int, t_double }; struct int_node { int value; } ; struct double_node { valeur double; }; 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; noeud struct n; n.type = t_int; n.int_n = i; renvoie 0; }
Et ce que je ne comprends pas, c'est ceci:
$ cc us.c $ cc -std = c99 us.c us.c: 18: 4: avertissement: déclaration ne déclare rien us .c: dans la fonction 'main': us.c: 26: 4: erreur: 'struct node' n'a pas de membre nommé 'int_n'
Utilisation de GCC
sans -std
l'option compile le code ci-dessus sans aucun problème (et le code similaire fonctionne plutôt bien), mais il semble que c99
ne permet pas cette technique. Pourquoi est-il ainsi et est-il possible de faire est c99
(ou c89
, c90
) compatible? Merci.
Les unions anonymes sont une extension GNU, ne faisant partie d'aucune version standard du langage C. Vous pouvez utiliser -std = gnu99 ou quelque chose comme ça pour les extensions c99 + GNU, mais il est préférable d'écrire correctement C et ne pas compter sur des extensions qui ne fournissent que du sucre syntaxique ...
Edit: Les unions anonymes ont été ajoutées en C11, elles font donc désormais partie intégrante du langage. Vraisemblablement -std=c11
vous permet de les utiliser.
Je trouve cette question environ un an et demi après que tout le monde l'a fait, donc je peux donner une réponse différente: les structures anonymes ne sont pas dans la norme C99, mais elles sont dans la norme C11. GCC et clang le supportent déjà (la norme C11 semble avoir supprimé la fonctionnalité de Microsoft, et GCC a pris en charge certaines extensions MSFT depuis un certain temps).
Eh bien, la solution consistait à nommer l'instance de l'union (qui peut rester anonyme comme type de données), puis à utiliser ce nom comme 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; -}; +} données; }; int main (void) { @@ -23,6 +23,6 @@ I.value = 10; Noeud struct n; N.type = t_int; - n.int_n = i; + n.data.int_n = i; renvoie 0; }
Maintenant, il se compile comme c99
sans aucun problème.
$ cc -std = c99 us.c $ $
Remarque: je ne suis pas satisfait de cette solution de toute façon.
Juste pour des clarifications sur anonyme struct
ou anonyme union
.
6.7.2.1 Spécificateurs de structure et d'union
Un membre sans nom dont le spécificateur de type est un spécificateur de structure avec aucune balise est appelé une structure anonyme ; un membre sans nom dont le spécificateur de type est un spécificateur d'union avec aucune balise est appelé union anonyme . Les membres d'une structure ou d'un syndicat anonyme sont considérés comme membres de la structure ou du syndicat conteneur. Cela s'applique récursivement si la structure ou l'union contenant est également anonyme.
C99 Il n'y a pas de structure ou d'union anonyme
Simplifié: Spécificateur de type Identifiant{
Liste de déclarations}
Tags;
struct
ou union
;struct
ou union
;struct
et anonymes union
typedef
devant le Type-specifier, les Tags sont des alias et non Tags.Il s'agit d'un struct
anonyme ou d'un union
anonyme uniquement s'il n'a pas d'identifiant et de balise, et existe dans un autre struct
ou 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
enfer: si vous avez un typedef
la partie tag n'est plus un tag, c'est un alias pour ce type.
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;
L'exemple ci-dessous change simplement struct
pour union
, fonctionne de la même manière.
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'
Une autre solution consiste à mettre la valeur d'en-tête commune (enum node_type type
) dans chaque structure et faites de votre structure de niveau supérieur une union. Ce n'est pas exactement "Ne vous répétez pas", mais cela évite à la fois les unions anonymes et les valeurs proxy inconfortables.
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;
}
En regardant 6.2.7.1 de C99, je constate que l'identifiant est facultatif:
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
J'ai fait des recherches de haut en bas et je ne trouve aucune référence à des syndicats anonymes contre la spécification. Le suffixe entier -opt indique que la chose, dans ce cas identifier
est facultative selon 6.1.
L'union doit avoir un nom et être déclarée comme ceci:
union UPair {
struct int_node int_n;
struct double_node double_n;
};
UPair X;
X.int_n.value = 12;