考虑以下代码:
struct s1 {unsigned short x;};
struct s2 {unsigned short x;};
union s1s2 { struct s1 v1; struct s2 v2; };
static int read_s1x(struct s1 *p) { return p->x; }
static void write_s2x(struct s2 *p, int v) { p->x=v;}
int test(union s1s2 *p1, union s1s2 *p2, union s1s2 *p3)
{
if (read_s1x(&p1->v1))
{
unsigned short temp;
temp = p3->v1.x;
p3->v2.x = temp;
write_s2x(&p2->v2,1234);
temp = p3->v2.x;
p3->v1.x = temp;
}
return read_s1x(&p1->v1);
}
int test2(int x)
{
union s1s2 q[2];
q->v1.x = 4321;
return test(q,q+x,q+x);
}
#include <stdio.h>
int main(void)
{
printf("%d\n",test2(0));
}
整个程序中存在一个联合对象--q
。它的活动成员先被设置为v1
,然后是v2
,再然后是v1
。代码只在该成员处于活动状态时使用q.v1
或所得到的指针上的地址运算符,同样也适用于q.v2
。由于p1
、p2
和p3
都是相同类型,因此使用p3->v1
访问p1->v1
应该是完全合法的,并且使用p3->v2
访问p2->v2
也是合法的。我没有看到任何可以证明编译器不能输出1234的东西,但是许多编译器(包括clang和gcc)生成输出4321的代码。我认为发生了什么是他们决定p3上的操作实际上不会改变内存中的任何位的内容,它们可以完全被忽略,但我没有看到标准中有任何可以证明忽略
p3
被用来从p1->v1
复制数据到p2->v2
和反之亦然的事实的东西。标准中是否有任何可以证明这种行为的东西,还是编译器只是没有遵循标准?
unsigned x
而不是unsigned short x
,你是否看到了相同的问题? - chux - Reinstate Monicap3
的操作,并因此失去了提供的别名相关信息。 - supercatunsigned
会以与unsigned short
类似的方式失败。使用unsigned
,我们可以放置任何“通常晋升”问题 - 这不应该影响到这个。 - chux - Reinstate Monica