2010-07-12 34 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