提问人:Some Name 提问时间:6/5/2019 最后编辑:timrauSome Name 更新时间:8/15/2022 访问量:1824
与具有灵活数组成员的匿名结构联合
Union with anonymous struct with flexible array member
问:
请考虑以下两个示例:
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)
匿名结构或工会的成员被视为 包含结构或联合的成员。
因此,我将措辞解释为,如果 a 包含匿名者作为成员,匿名者的成员将被视为包含 .union
struct
struct
union
问题:为什么第一个示例编译良好,而不是像第二个示例那样失败?
答:
第二种情况无法编译,因为灵活数组成员是结构类型的属性,而不是联合的属性。这很简单。
接下来,在第一种情况下,尝试访问将是未定义的行为,因为没有为此分配内存。b[0]
引用 , §6.7.2.1/P18C11
作为特殊情况,具有多个命名成员的结构的最后一个元素可能 数组类型不完整;这称为灵活数组成员。[...]如果此数组没有元素,则其行为类似于 它有一个元素,但如果尝试访问该元素,则行为是未定义的 元素或生成一个指针。
可是
匿名结构或联合的成员被视为包含结构或联合的成员。
也就是说,出于访问目的,布局保持不变。看,在你的第一个例子中,你正在访问(和),就好像他们是工会的直接成员一样。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
}
评论
union
(C11) 标准在 §6.7.2.1 结构和联合说明符 ¶3 中说 — 约束:
¶3 结构或联合不应包含具有不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可以包含指向其自身实例的指针),但具有多个命名成员的结构的最后一个成员可能具有不完整的数组类型;这种结构(以及可能递归地包含作为这种结构的成员的任何并集)不应是结构的成员或数组的元素。
请注意,只有结构可以(直接)包含灵活的数组成员,而联合则不能。
第一种情况是合法的。第二个不是。
(§6.7.2.1 ¶18 定义了术语“灵活数组成员”。
顺便说一句,在问题的第一个版本中,第一个示例中的语句访问了数组中未分配的元素——该缺陷已在修订版 2 中得到修复。写入会给出一个大小为 0 的数组。您必须使用动态内存分配(或某些其他机制)来为非空 FAM 分配具有足够空间的联合或结构。 类似的评论也适用于第二个示例。printf()
union test test;
但是,由于在第一种情况下,结构是匿名的,因此结构的成员应被视为包含联合的成员,从而使联合包含灵活的数组成员。正如我所引用的:匿名结构或工会的成员被认为是包含结构或工会的成员。
请注意,匿名结构不会仅仅因为嵌入到联合中而失去其形状。一个区别是 in 的偏移量不能为 0——这与工会的普通成员完全不同。通常,联合的所有成员都从偏移量 0 开始。不过,大多数情况下,这表示给定一个变量,您可以引用 和 。在过去,您必须为结构指定一个名称:并且已经使用或访问联合中结构的元素。这不会影响允许灵活数组成员的位置;仅用于访问它的符号。b
union test
union test u;
u.a
u.b
union test { struct { int a; int b[]; } s; };
u.s.a
u.s.b
评论
b
union test
union test u;
u.a
u.b
union test { struct { int a; int b[]; } s; };
u.s.a
u.s.b
union foo
char
union foo x
注:本答复自首次编写以来已作了实质性修改,反映出在答复的原始版本所依据的文件公布后,委员会的立场发生了变化。
匿名结构或联合的成员被视为包含结构或联合的成员。
这是一个难以解释的条款,事实上,它至少是两份针对该标准的缺陷报告的主题。正如委员会在对DR 499的回应中所支持的那样,其意图是,出于布局目的,匿名结构被视为结构本身是包含结构或联盟的成员,但对其成员的访问被表示为好像他们是包含结构或联盟的成员。
另一方面,DR 502 的公认立场认为,即使是包含灵活数组成员作为其唯一成员的匿名结构,如果它是包含它的结构的最后一个成员(不是联合),并且至少有一个其他成员在它之前,也是允许的。
我发现这些有点不一致,但它们的统一主题似乎是该领域标准的意图归结为布局。匿名结构中的灵活数组成员是允许的,只要它位于最内层命名结构或联合的布局的末尾,考虑到其他成员,该结构必须具有非零大小,同时考虑到匿名结构的成员不重叠的事实,无论匿名结构是否出现在联合内部。
拟议的委员会对 DR 502 的回应(与其最初的立场不同)与此一致。它认为,结构或联合内部的匿名结构必须遵守与其他结构在灵活数组成员方面相同的规则,尽管您询问了相关规定。
委员会似乎没有决定你提出的具体问题,但其决定的主题似乎很明确:“被视为包含结构或工会的成员”的措辞旨在狭义地解释为仅关于访问匿名结构和工会成员的语法的声明。因此,该条文没有说明匿名结构是否可能包含FAM,以及关于何时何地适用FAM的一般规则。这些规则允许你的第一个案例。
评论
struct { int i; struct { int a[]; }; };
struct { int i; union { int a[]; };};
struct { ...; union { char* c; Node* n[0]; }; };
当然,匿名复合材料中未标记的复合材料始终保持其形状。但是,§6.7.2.1p13的措辞中没有明确说明这一点。措辞已修订为:(着重号后加):
- 类型说明符是没有标记的结构说明符的未命名成员称为匿名结构;类型说明符是不带标记的联合说明符的未命名成员称为匿名联合。匿名结构或联合的成员被视为包含结构或联合的成员,保留其结构或联合布局。如果包含结构或联合也是匿名的,则以递归方式应用。
有关C18标准和免费提供的草案的链接,请参阅 http://www.iso-9899.info/wiki/The_Standard (pdf)
评论
u
um
T
sm
T *p = &u->m; p->sm = 123;
u->m.sm = 123;
p
p
评论