C标准关于两个相同类型的联合体有什么规定?

12

是否可能在任何编译器及架构上使以下断言失败?

union { int x; int y; } u;
u.x = 19;
assert(u.x == u.y);

2
你的标题与你在帖子中提出的问题有什么关系?至于失败...由于.x和.y占用相同的内存并且都被视为相同的类型,我不明白除非assert(u.x == u.x);失败了,否则断言怎么会失败。 - mah
@mah,我试图给出一个标题的例子,我相信它们至少是相关的。 - perreal
@mah:两个东西占用相同的内存并具有相同的类型并不意味着它们在C模型中看起来相等。考虑 void foo(restrict int *a, restrict int *b) { *b = 3; assert(*b == *a); } int main(void) { int x; foo(&x, &x); return 0; }。尽管 *a*b 引用内存中的同一对象并具有相同的类型,但是断言可能会失败,因为C实现不需要在将值赋给 *b 后“查找”内存中的 *a。同样,如果不是联合体必须重新解释字节,则 u.x == u.y 可能会失败。 - Eric Postpischil
@mah:*a*b是占用相同内存并具有相同类型的对象。它们通过指针获得这一事实与此无关;它们都是左值。 - Eric Postpischil
@EricPostpischil,这非常相关,因为您提到了C语言缺乏重新读取它认为已经知道值的项目的要求(当未使用volatile关键字时)。这是实际问题的一个次要争论。如果您试图表达您或您的知识更加优越,我会省去您的麻烦,并在两个方面都同意您的观点。 - mah
显示剩余2条评论
3个回答

6

C99为一个特殊情况提供了保证,当联合体的两个成员是共享字段初始序列的结构时:

struct X {int a; /* other fields may follow */ };
struct Y {int a; /* other fields may follow */ };
union {X x; Y y;} u;
u.x.a = 19;
assert(u.x.a == u.y.a); // Guaranteed never to fail by 6.5.2.3-5.
6.5.2.3-5 : 为了简化联合的使用,有一个特殊的保证:如果一个联合包含多个结构体,这些结构体共享一个公共的初始序列(见下文),并且如果联合对象当前包含其中一个结构体,则允许在任何声明联合完整类型可见的地方检查它们的任何一个公共初始部分。如果相应成员具有兼容类型(对于位域,还需要具有相同的宽度)的一个或多个初始成员的序列,则两个结构体共享一个公共的初始序列。

然而,我无法找到关于联合内非结构化类型的类似保证。虽然这可能是一个疏漏:如果标准详细描述了不同的结构化类型必须发生的情况,那么它应该澄清相同点,以便使更简单的非结构化类型也能够符合标准。


1
有人可能会认为,这表明委员会认为从intint的类型转换不会改变值,因此在6.5.2.3-5附近没有指定。 - Pascal Cuoq
1
访问联合体中存储的不是最后一个成员的情况在C 2011(N1570)注释95中被明确地涉及,该注释指向6.2.6条款。6.5.2.3条款与此无关。 - Eric Postpischil

4
问题中的assert在标准C实现中永远不会失败,因为在对u.x进行赋值后访问u.y需要将u.x的字节重新解释为u.y的类型。由于类型相同,重新解释产生相同的值。
这个要求在C 2011(N1570)6.5.2.3注释95中有所提及,该注释指出它源自条款6.2.6,该条款涵盖类型的表示。注释95说:

如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的适当部分重新解释为新类型的对象表示,如6.2.6所述(有时称为“类型游戏”)。这可能是一个陷阱表示。

(N1570是一个非官方的草案,但易于在网络上获取。)

3

我认为以你期望的方式回答这个问题非常困难。

据我所知,读取一个联合体(union)中不是最近写入的字段是未定义行为。

因此,无法简单回答“否”,因为任何编译器都可以自由地检测到这种情况并在其内部失败,如果他们觉得这样做有必要的话。


2
然而,有一件事情需要注意:指向联合体对象的指针经过适当转换后,可以指向它的每个成员(或者如果成员是位域,则指向其所在的单元),反之亦然。 - perreal
1
这不是未定义行为。自1999年TC3起,C标准要求访问联合体成员时,除了最后一个存储的成员外,重新解释字节作为所访问类型的值。请参见草案N1570 6.5.2.3注95或N1256注82。 - Eric Postpischil
2
@EricPostpischil 这是脚注82,仅在C99的TC3修订版中发布。 - Pascal Cuoq
@EricPostpischil,同意。但是如果我们创建一个指向u的指针,它应该指向x的第一个字节,也指向y的第一个字节吗? - perreal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接