2010-07-12 11 views
37

hier ist sehr Code Problem vereinfacht die ich habe:Anonyme Union in Struct nicht 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; 
} 

Und was ich undestand nicht wissen, ist dies:

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

Mit GCC ohne -std Option Code oben ohne Probleme kompiliert (und die ähnlicher Code funktioniert ziemlich gut), aber es scheint, dass c99 diese Technik nicht erlaubt. Warum ist es so und ist es möglich, c99 (oder c89, c90) kompatibel zu machen? Vielen Dank.

+1

Nur eine Anmerkung, clang kompiliert gegebenen Code mit und ohne '-std = c99' still, ohne irgendwelche Fehler und Warnungen. – Martin

Antwort

2

Union muss einen Namen haben und wie folgt deklariert werden:

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

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

Nicht in C11, aber in C99 ja. Aber seit wir die Drei-Jahres-Marke seit seiner Veröffentlichung überschritten haben, ist es vielleicht an der Zeit, -std = c11 zu übergeben :). –

50

Anonyme Gewerkschaften sind eine GNU-Erweiterung, nicht Teil einer Standard-Version der C-Sprache. Sie können -std verwenden = gnu99 oder so ähnlich für c99 + GNU-Erweiterungen, aber es ist am besten geeignete C zu schreiben und nicht auf Erweiterungen verlassen, die nichts anderes als syntaktischer Zucker liefern ...

Edit: Anonyme Gewerkschaften wurden hinzugefügt in C11 sind sie jetzt ein Standardteil der Sprache. Vermutlich können Sie die GCC -std=c11 verwenden.

4

Nun, die Lösung war, Instanz der Union zu nennen (die als Datentyp anonym bleiben kann) und dann diesen Namen als Proxy zu verwenden.

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

Jetzt stellt es als c99 ohne Probleme.

 
$ cc -std=c99 us.c 
$ 

Hinweis: sowieso Ich bin nicht glücklich über diese Lösung.

+3

Sie sollten glücklich sein! Es ist die Standardmethode für den Zugriff auf Union-Mitglieder, die seit dem 1. Januar 1970 mit jedem C-Compiler zusammenarbeiten. – Jens

+2

Es bringt den Code etwas nach, keine Ahnung, warum es nicht in K & R C enthalten war, scheint mir ein einfaches und nützliches Feature zu sein ... Wie auch immer, ich benutze die gleiche Proxy-Methode, aber definiere Makros, um alle Eingaben zu vermeiden. –

+2

Ich weiß, dass dies ein sehr alter Beitrag ist, aber das Kopieren des eigentlichen Codes anstelle eines Diff-Patches ist viel besser lesbar. – ysap

0

auf 6.2.7.1 von C99 Sehen, ich sehe, dass die Kennung optional:

  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 

Ich bin oben und unten suchen, und kann jede Referenz nicht gegen das Wesen anonyme Gewerkschaften finden Spez. Das Suffix -opt gibt an, dass das Ding, in diesem Fall identifier, gemäß 6.1 optional ist.

+4

Ich glaube, hier liegt ein Missverständnis. Der Bezeichner für eine Struktur oder eine Union ** Tag ** ist optional, aber nicht der Bezeichner, der deklariert wird. Sie können nicht sagen, "union {...};" innerhalb eines Aggregats aus dem gleichen Grund, dass Sie nicht "int;" sagen können. Im Union-Fall erlauben Compiler-Erweiterungen dies, da Sie bei Verwendung einer anonymen Union Bezeichner im '{...}' -Teil verwenden können. – Jens

21

Ich finde diese Frage etwa anderthalb Jahre nach jeder anderen, also kann ich eine andere Antwort geben: anonyme Strukturen sind nicht im C99-Standard, aber sie sind im C11-Standard. GCC und clang unterstützen dies bereits (der C11-Standard scheint die Funktion von Microsoft aufgehoben zu haben, und GCC hat einige MSFT-Erweiterungen für einige Zeit unterstützt).

1

Eine andere Lösung besteht darin, den gemeinsamen Header-Wert (enum node_type type) in jede Struktur zu setzen und Ihre Top-Level-Struktur eine Union zu machen. Es ist nicht genau "Do not Repeat Yourself", aber es vermeidet sowohl anonyme Verbindungen als auch unangenehm aussehende Proxy-Werte.

+0

Für die späten Leser wie mich: DRY könnte vermieden werden, indem Sie eine Vorlage und geeignete typedefs verwenden: 'template struct base_node {/ *[...]*/ T Wert;}; typedef base_node int_node; ' – Aconcagua

+3

In C++ vielleicht, aber nicht in C99. – theJPster

+0

Ah, tut mir leid, irgendwie habe ich vergessen, in C zu sein, während ich gelesen und darüber nachgedacht habe ... Ich war letztendlich zu tief in C++. – Aconcagua