是否可能在任何编译器及架构上使以下断言失败?
union { int x; int y; } u;
u.x = 19;
assert(u.x == u.y);
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 : 为了简化联合的使用,有一个特殊的保证:如果一个联合包含多个结构体,这些结构体共享一个公共的初始序列(见下文),并且如果联合对象当前包含其中一个结构体,则允许在任何声明联合完整类型可见的地方检查它们的任何一个公共初始部分。如果相应成员具有兼容类型(对于位域,还需要具有相同的宽度)的一个或多个初始成员的序列,则两个结构体共享一个公共的初始序列。
然而,我无法找到关于联合内非结构化类型的类似保证。虽然这可能是一个疏漏:如果标准详细描述了不同的结构化类型必须发生的情况,那么它应该澄清相同点,以便使更简单的非结构化类型也能够符合标准。
int
到int
的类型转换不会改变值,因此在6.5.2.3-5附近没有指定。 - Pascal Cuoqassert
在标准C实现中永远不会失败,因为在对u.x
进行赋值后访问u.y
需要将u.x
的字节重新解释为u.y
的类型。由于类型相同,重新解释产生相同的值。(N1570是一个非官方的草案,但易于在网络上获取。)如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的适当部分重新解释为新类型的对象表示,如6.2.6所述(有时称为“类型游戏”)。这可能是一个陷阱表示。
我认为以你期望的方式回答这个问题非常困难。
据我所知,读取一个联合体(union)中不是最近写入的字段是未定义行为。
因此,无法简单回答“否”,因为任何编译器都可以自由地检测到这种情况并在其内部失败,如果他们觉得这样做有必要的话。
assert(u.x == u.x);
失败了,否则断言怎么会失败。 - mahvoid 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*a
和*b
是占用相同内存并具有相同类型的对象。它们通过指针获得这一事实与此无关;它们都是左值。 - Eric Postpischil