2010-07-12 71 views
37

這裏很簡單的問題,我的代碼有:結構中的匿名聯合不在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; 
} 

什麼我也不是已瞭解此:

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

使用GCC沒有-std選項編譯代碼上面沒有任何問題(和類似的代碼工作得很好),但似乎c99不允許這種技術。爲什麼它是如此,是否有可能使c99(或c89c90)兼容?謝謝。

+1

只是一個說明,鏗鏘編寫代碼帶和不帶'-std = c99'靜默,沒有任何錯誤和警告。 – Martin 2010-07-12 11:58:21

回答

2

聯盟必須有一個名稱,並聲明如下:

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

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

不是在C11中,而是在C99中。是的。但是自從它發佈以來我們已經過了三年的時間,也許是時候開始傳遞-std = c11 :)。 – 2014-12-03 03:14:10

50

匿名工會是一個GNU擴展,而不是C語言的任何標準版本的一部分。您可以使用-std = gnu99或類似的東西爲C99 + GNU擴展,但它是最好寫正確的C和不依賴於它提供什麼,但語法糖擴展...

編輯:添加匿名聯合在C11中,所以它們現在是語言的標準部分。據推測GCC的-std=c11可以讓你使用它們。

4

那麼,解決方案是命名聯合實例(它可以保持匿名作爲數據類型),然後使用該名稱作爲代理。

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

現在它編譯爲c99沒有任何問題。

 
$ cc -std=c99 us.c 
$ 

注意:無論如何我對此解決方案並不滿意。

+3

你應該高興!這是訪問工會成員的標準方式,自1970年1月1日起保證可以與任何C編譯器一起工作。 – Jens 2012-08-23 07:11:31

+2

它在代碼中有點不明白,不知道它爲什麼不包含在K&R C中,似乎對我來說是一個簡單而有用的功能......無論如何,我使用相同的代理方法,但是定義了宏以避免所有的輸入。 – 2014-12-03 03:16:52

+2

我意識到這是一個非常古老的帖子,但是複製實際代碼而不是diff修補程序更具可讀性。 – ysap 2016-04-25 01:10:18

0

看C99的6.2.7.1,我看到標識符是可選的:

  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 

我一直在向上和向下搜索,並不能找到匿名工會是反對任何參考規範。整個-opt後綴表示該事物,在這種情況下identifier根據6.1是可選的。

+4

我認爲這裏有一個誤解。結構或聯合**標籤**的標識符是可選的,但不是正在聲明的標識符。你不能說'union {...};'因爲你不能說'int;'的同樣原因在一些集合中。在union情況下,編譯器擴展允許使用它,因爲在使用匿名聯合時,可以在'{...}'部分使用標識符。 – Jens 2012-08-22 08:39:04

21

我在其他人做了大約一年半的時間後發現了這個問題,所以我可以給出一個不同的答案:匿名結構不在C99標準中,但它們是C11標準。 GCC和clang已經支持這一點(C11標準似乎已經提升了微軟的功能,並且GCC已經爲一些MSFT擴展提供了一段時間的支持)。

1

另一種解決方案是將公共標題值(enum node_type type)放入每個結構中,並使頂層結構成爲聯合。這不完全是「不要重複自己」,但它確實避免了匿名聯合和不舒服的代理值。

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

對於像我這樣的後期讀者:DRY可以通過使用模板和適當的typedef來避免:'template struct base_node {/*[...]*/ T value;}; typedef base_node int_node;' – Aconcagua 2015-09-15 14:43:05

+3

可能在C++中,但不在C99中。 – theJPster 2015-10-02 16:13:05

+0

啊,抱歉,不知何故,在閱讀和思考C時忘記了在C中......我最終在C++方面做得太深了。 – Aconcagua 2015-10-03 08:29:42