这里有一个非常简化的问题代码:
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
(或c89
,c90
)兼容?感谢。
答案 0 :(得分:60)
匿名联合是GNU扩展,不是C语言的任何标准版本的一部分。对于c99 + GNU扩展,你可以使用-std = gnu99或类似的东西,但是最好编写适当的C而不是依赖于只提供语法糖的扩展......
编辑:匿名工会已在C11中添加,因此它们现在是该语言的标准部分。据推测,GCC的-std=c11
允许你使用它们。
答案 1 :(得分:25)
我发现这个问题大约在其他人做了一年半之后,所以我可以给出一个不同的答案:匿名结构不符合C99标准,但它们符合C11标准。 GCC和clang已经支持这一点(C11标准似乎已经取消了微软的功能,GCC已经为一些MSFT扩展提供了一段时间的支持。)
答案 2 :(得分:5)
嗯,解决方案是命名union的实例(可以作为数据类型保持匿名),然后使用该名称作为代理。
$ 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 :(得分:2)
仅用于说明匿名struct
或匿名union
。
6.7.2.1结构和联合说明符
未命名成员,其类型说明符是具有以下内容的结构说明符 没有标签称为匿名结构; 未命名成员,其类型 说明符是带有没有标签的联合说明符,称为匿名 联盟。匿名结构或联合的成员被认为 成为包含结构或联合的成员。这适用 如果包含的结构或联合也是匿名的,则递归地进行。
C99 没有匿名结构或联合
简化:类型说明符 标识符 {
声明列表 }
标签 ;
struct
或union
; struct
或union
的自定义名称; struct
和匿名union
typedef
,则 Tags 是别名而不是 Tags 。只有匿名标识符struct
或union
内没有标识符和标签,它才是匿名struct
或匿名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
地狱:如果您有typedef
,则标签部分不再是标签,它是该类型的别名。
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;
下面的示例只是将struct
的{{1}}更改为相同的方式。
union
答案 4 :(得分:1)
Union必须有一个名字并且声明如下:
union UPair {
struct int_node int_n;
struct double_node double_n;
};
UPair X;
X.int_n.value = 12;
答案 5 :(得分:1)
查看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。
答案 6 :(得分: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;
}