Som*_*ame 14 c union struct language-lawyer
请考虑以下两个示例:
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?
}
Run Code Online (Sandbox Code Playgroud)
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]);
}
Run Code Online (Sandbox Code Playgroud)
行为尚不清楚。我希望这些示例的行为相同(即,第一个示例也将无法编译),因为6.7.2.1(p13):
匿名结构或联合的成员被视为包含结构或联合的成员。
因此,我将措辞解释为,好像一个union包含匿名者struct的成员,该匿名struct者的成员将被视为包含成员的成员union。
问题:为什么第一个示例可以很好地编译,而不是像第二个那样失败?
Joh*_*ger 13
匿名结构或联合的成员被视为包含结构或联合的成员。
这是一个难以理解的规定,并且确实是至少两次针对该标准的缺陷报告的主题。委员会在对DR 499的答复中支持的意图是,将匿名结构视为用于布局目的,就好像该结构本身是包含结构或联合的成员一样,但是对其成员的访问被表示为好像它们是成员一样。包含结构或联合体。
另一方面,在DR 502上的可接受位置认为,即使包含弹性数组成员作为其唯一成员的匿名结构也是允许的,如果它是结构的最后一个成员(而非联合),并且包含至少另一个在它之前。
我发现这些不一致,但是它们之间的统一主题似乎是该领域标准的意图归结于布局。只要匿名结构内部的柔性数组成员位于最里面的命名结构或联合的布局的末尾即可,考虑到成员的事实,该数组或联合的大小必须考虑其他成员,且大小必须为非零无论匿名结构是否出现在并集内,匿名结构的都不会重叠。
第二种情况无法编译,因为灵活数组成员是结构类型的属性,而不是联合的属性。那很简单。
接下来,在第一种情况下,尝试访问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
}
Run Code Online (Sandbox Code Playgroud)
(C11)标准在第6.7.2.1节“结构和联合说明符”¶3中作了规定 -一个约束:
¶3结构或联合不得包含具有不完整或函数类型的成员(因此,结构不得包含自身的实例,但可以包含指向其自身实例的指针),但结构的最后一个成员具有一个以上的命名成员可能具有不完整的数组类型;这样的结构(以及可能包含递归关系的任何并集的任何并集)都不应该是结构的成员或数组的元素。
请注意,只有结构可以(直接)包含灵活的数组成员-联合不能。
第一种情况是合法的。第二个不是。
(第6.7.2.1节¶18定义了术语灵活数组成员。)
顺便说一句,在第一个版本的问题中,printf()第一个示例中的语句正在访问未分配的数组元素—此缺陷已在修订版2中得到修复。写入union test test;将为您提供大小为0的数组。您必须使用动态内存分配(或其他某种机制)为非空FAM分配具有足够空间的并集或结构。类似的注释也适用于第二个示例。
但是由于在第一种情况下该结构是匿名的,因此应将结构的成员视为包含并的成员,从而使该联合包含一个灵活的数组成员。正如我引用的那样,匿名结构或联合的成员被视为包含结构或联合的成员。
请注意,匿名结构不会仅仅因为嵌入到联合中而失去其形状。一个区别是bin 的偏移量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访问该结构的元素。这不会影响允许使用灵活数组成员的位置。仅用于访问它的符号。