2010-07-12 13 views
37

qui è molto codice di problema semplificato ho:Unione anonima all'interno di struct non in 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; 
} 

E quello che non undestand è questo:

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

Utilizzando GCC senza -std opzione compila codice di cui sopra senza problemi (e la codice simile funziona abbastanza bene), ma sembra che c99 non consenta questa tecnica. Perché è così ed è possibile rendere c99 (o c89, c90) compatibile? Grazie.

+1

Solo una nota, clang compila codice fornito con e senza '-std = c99' in modo silenzioso, senza errori e avvisi. – Martin

risposta

2

Unione deve avere un nome e essere dichiarati in questo modo:

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

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

Non in C11, ma in C99 sì. Ma dal momento che abbiamo superato il marchio di tre anni dalla sua uscita, forse è il momento di iniziare a passare -std = c11 :). –

50

unioni anonime sono un'estensione GNU, non fa parte di alcuna versione standard del linguaggio C. È possibile utilizzare -std = gnu99 o qualcosa di simile per le estensioni C99 + GNU, ma è meglio scrivere corretta C e non fare affidamento sulle estensioni che forniscono altro che zucchero sintattico ...

Edit: sono stati aggiunti sindacati anonimi in C11, quindi sono ora una parte standard della lingua. Presumibilmente GCC -std=c11 consente di utilizzarli.

4

Bene, la soluzione era denominare l'istanza dell'unione (che può rimanere anonima come tipo di dati) e quindi utilizzare tale nome come 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; 
} 

Ora compila come c99 senza problemi.

 
$ cc -std=c99 us.c 
$ 

Nota: io non sono contento di questa soluzione in ogni caso.

+3

Dovresti essere felice! È il modo standard per accedere ai membri del sindacato, garantito per funzionare con qualsiasi compilatore C dal 1 ° gennaio 1970. – Jens

+2

È un po 'strano il codice, non ho idea del motivo per cui non è stato incluso in K & R C, mi sembra una funzionalità semplice e utile ... Ad ogni modo, utilizzo lo stesso metodo proxy ma definisco macro per evitare tutta la digitazione. –

+2

Mi rendo conto che questo è un post molto vecchio, ma la copia del codice effettivo invece di una patch diff è molto più leggibile. – ysap

0

Guardando 6.2.7.1 di C99, sto vedendo che l'identificatore è opzionale:

  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 

Sono stato su e giù per la ricerca, e non riesce a trovare alcun riferimento alle unioni anonime essere contro la spec. L'intero suffisso -opt indica che la cosa, in questo caso, identifier è facoltativa in base a 6.1.

+4

Penso che ci sia un equivoco qui. L'identificatore per un tag struct o union ** è facoltativo, ma non l'identificatore che viene dichiarato. Non puoi dire "unione {...};" all'interno di alcuni aggregati per lo stesso motivo non puoi dire "int". Nel caso dell'unione, le estensioni del compilatore lo consentono, perché puoi usare gli identificatori nella parte '{...}' quando usi un sindacato anonimo. – Jens

21

Sto trovando questa domanda circa un anno e mezzo dopo che tutti gli altri hanno fatto, quindi posso dare una risposta diversa: le strutture anonime non sono nello standard C99, ma sono nello standard C11. GCC e clang già supportano questo (lo standard C11 sembra aver rimosso la funzionalità da Microsoft, e GCC ha fornito supporto per alcune estensioni MSFT per qualche tempo).

1

Un'altra soluzione consiste nel mettere il valore di intestazione comune (enum node_type type) in ogni struttura e rendere la struttura di livello superiore un'unione. Non è esattamente "Do not Repeat Yourself", ma evita sia le unioni anonime che i valori proxy dall'aspetto scomodo.

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

Per gli ultimi lettori come me: DRY potrebbe essere evitato usando un template e typedef appropriati: 'template struct base_node {/*[...]*/ T value;}; typedef base_nodo int_node; ' – Aconcagua

+3

In C++ forse, ma non in C99. – theJPster

+0

Ah, mi dispiace, in qualche modo ho dimenticato di essere in C mentre leggevo e pensavo a ... Sono stato troppo profondo in C++ alla fine. – Aconcagua