在C ++中,如果A
和B
是不同的类,那么将A*
强制转换为B*
然后取消引用(即给定1)通常是未定义的行为p>
A *a = ...;
写
是未定义的行为((B*)a)->x
即使发生这种情况,x
在两个类中都有相同的类型和偏移量。
另一方面,char*
特别豁免施法规则。这是否意味着如果你这样写的话,它的定义是否明确?
((B*)(char*)a)->x
答案 0 :(得分:3)
此问题取决于A
和B
的类型。
如果B
是A
的基类,或A
是B
的基类,则((B *)a)
是静态广播,因此{如果((B *)a)->x
是基类,则{1}}是正确的;或者如果B
是派生类,B
实际上指向a
类型对象的A
部分。
如果B
和A
是不相关的类型,那么我们必须查看严格别名规则。这是一些具体的代码:
B
假设断言通过,我们知道struct A { int p,q,r; } a;
struct B { int z,y; } b;
static_assert( sizeof a == 3 * sizeof(int) );
static_assert( sizeof b == 2 * sizeof(int) );
必须指定与((B *)&a)->y
相同的内存位置。
这里有两种思想流派:
a.q
是((B *)&a)->y
类型的左值,int
是a.q
类型的对象。 int
与int
兼容,因此不存在别名冲突。
评估int
表示((B *)&a)->y
,它首先评估(*(B *)&a).y
,这违反了严格的别名规则,因为*(B *)&a
未指定包含{{{1}的内存位置1}}。
我个人跟(1)一起去;我不认为案例2中的评估被视为“访问”,严格的别名规则谈到“访问”:
如果程序试图通过以下类型之一以外的glvalue 访问对象的存储值,则行为未定义:
然而,有一个灰色区域,因为虽然阅读*(B *)&a
访问B
对象的存储值;目前尚不清楚它是否也被视为访问包含该对象的结构的存储值的一部分。
((B *)&a)->y
对后一种情况没有任何影响,我们最终仍然得到了int
类型的左值。在继承的情况下,如果它是多重继承,那么这可能会破坏代码,因为它将((B*)(char*)a)->x
变为int
。