请考虑以下两个示例:
1。
union test{
struct {
int a;
int b[];
};
};
int main(void){
union test test;
test.a = 10;
printf("test.b[0] = %d", test.b[0]); //prints 0, UB?
}
2。
#include <stdio.h>
union test{
int a;
int b[]; //error: flexible array member in union
};
int main(void){
union test test;
test.a = 10;
printf("test.b[0] = %d", test.b[0]);
}
该行为尚不清楚。我希望这些示例的行为相同(即第一个示例也无法编译),因为6.7.2.1(p13)
:
匿名结构或联合的成员被认为是 包含结构或联合的成员。
因此,我将措辞解释为:union
包含匿名struct
作为成员,匿名struct
的成员将被视为包含union
的成员
问题::为什么第一个示例可以很好地编译,而不是像第二个示例那样失败?
答案 0 :(得分:13)
匿名结构或联合的成员被视为包含结构或联合的成员。
这是一个难以理解的规定,实际上,它至少是针对该标准的两次缺陷报告的主题。委员会在对DR 499的回复中表示支持的意图是,将匿名结构出于布局目的进行处理,就好像该结构本身是包含结构或联合的成员,但是 access 它的成员表示为好像它们是包含结构或联合的成员。
另一方面,DR 502上的可接受位置认为,即使是一个包含灵活数组成员作为其 only 成员的匿名结构,如果它是包含它的结构(非联合),并且在它之前至少还有一个。
我发现这些不一致,但是它们之间的统一主题似乎是该领域标准的意图归结为 layout 。只要匿名结构内部的弹性数组成员位于最里面的命名结构或联合的布局的末尾即可,考虑到成员的事实,该结构或联合的大小在考虑其他成员时必须具有非零大小无论匿名结构是否出现在并集内,匿名结构都不会重叠。
答案 1 :(得分:6)
第二种情况无法编译,因为灵活数组成员是结构类型的属性,而不是联合的属性。那很简单。
接下来,在第一种情况下,尝试访问b[0]
将是未定义的行为,因为尚未为此分配内存。
引用C11
,第6.7.2.1/P18
在特殊情况下,结构中具有多个命名成员的最后一个元素可能 数组类型不完整;这称为灵活数组成员。 [...]如果此数组不包含任何元素,则其行为就像 它只有一个元素,但是如果尝试访问该元素,则行为是不确定的 元素或生成一个指针。
那是
匿名结构或联合的成员被视为包含结构或联合的成员。
这是出于访问目的,布局保持不变。在第一个示例中,您正在访问a
(和b
),就好像它们是联合的直接成员一样。
为了澄清,
#include <stdio.h>
union test{
struct {
int p;
float q;
} t; //named structure member
struct {
int a;
int b[];
};
char pqr;
};
int main(void){
union test test;
test.t.p = 20; // you have to use the structure member name to access the elements
test.pqr = 'c'; // direct access, as member of union
test.a = 10; // member of anonymous structure, so it behaves as if direct member of union
}
答案 2 :(得分:6)
(C11)标准在§6.7.2.1 Structure and union specifiers ¶3中说-一个约束:
¶3结构或联合不得包含具有不完整或函数类型的成员(因此,结构不得包含其自身的实例,但可以包含指向其自身的实例的指针),但最后一个成员是具有多个命名成员的结构可能具有不完整的数组类型;这样的结构(以及可能包含递归关系的任何并集的任何并集)都不应该是结构的成员或数组的元素。
请注意,只有结构可以(直接)包含灵活的数组成员-联合不能。
第一种情况是合法的。第二个不是。
(§6.7.2.1 ¶18定义了柔性数组成员。)
偶然地,在问题的第一个version中,第一个示例中的printf()
语句正在访问未分配的数组元素-此缺陷已在修订版2中修复编写union test test;
会得到一个大小为0的数组。您必须使用动态内存分配(或其他一些mechanism)来分配具有足够空间的联合或结构, -空FAM。类似的注释也适用于第二个示例。
但是由于在第一种情况下该结构是匿名的,因此应将结构的成员视为包含并的成员,从而使该联合包含一个灵活的数组成员。正如我引述的,匿名结构或联合的成员被视为包含结构或联合的成员。
请注意,匿名结构不会仅仅因为嵌入到联合中而变形。一个区别是b
中union test
的偏移量不能为0,这与联合的普通成员完全不同。通常,联合的所有成员都从偏移量0开始。不过,大多数情况下,这表示给定变量union test u;
,您可以引用u.a
和u.b
。过去,您必须为结构指定一个名称:union test { struct { int a; int b[]; } s; };
,并使用u.s.a
或u.s.b
来访问并集内结构的元素。这不会影响允许使用灵活数组成员的位置。仅用于访问它的符号。
答案 3 :(得分:3)
毫无疑问,匿名合成物中未加标签的合成物始终会保持其形状。但这在第6.7.2.1p13节中并未明确。将该措辞修改为C18锡,以:(强调):
- 其类型说明符是没有标签的结构说明符的未命名成员称为匿名结构;其类型说明符是没有标签的联合说明符的未命名成员称为匿名联合。匿名结构或联合的成员被视为包含结构或联合的成员,保持其结构或联合的布局。如果包含结构或联合也是匿名的,则递归适用。
有关C18标准和http://www.iso-9899.info/wiki/The_Standard的链接,请参见freely-available draft (pdf)