web-dev-qa-db-fra.com

Union anonyme dans la structure pas en c99?

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.

49
Martin

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.

62
R..

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).

25
bk.

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.

5
Martin

Juste pour des clarifications sur anonyme struct ou anonyme union.

C11

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;

  • Spécificateur de type: struct ou union;
  • Identifiant: facultatif, votre nom personnalisé pour le struct ou union;
  • Declaration-list: membres, vos variables, anonymes struct et anonymes union
  • Tags: facultatif. Si vous avez un 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'

2
Undefined Behavior

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

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.

1
jer

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;
1
Svisstack