在 C++ 中,如果 A
和 B
是不同的类,将 A*
转换为 通常是未定义的行为B*
然后解引用,即给定
A *a = ...;
写是未定义的行为
((B*)a)->x
即使碰巧 x
在两个类中具有相同的类型和偏移量。
另一方面,char*
对转换规则有特殊的豁免。这么写是不是定义明确了?
((B*)(char*)a)->x
最佳答案
这道题取决于A
和B
的类型。
如果B
是A
的基类,或者A
是B
的基类,那么((B *)a)
是静态转换,所以如果 B
是 ((B *)a)->x
是正确的基类;或者如果 B
是派生类并且 a
实际上指向类型为 B
的对象的 A
部分。
如果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
是a.q
的对象输入int
。int
与int
兼容,因此不存在别名冲突。Evaluating
((B *)&a)->y
表示(*(B *)&a).y
,它首先计算* (B *)&a
,这违反了严格的别名规则,因为*(B *)&a
没有指定包含B
的内存位置。
我个人喜欢 (1);我不认为情况 2 中的评估算作“访问”,严格的别名规则谈论“访问”:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
但是这里有一个灰色区域,因为虽然读取 ((B *)&a)->y
访问的是 int
对象的存储值;目前尚不清楚它是否也被视为访问包含该对象的结构的存储值的一部分。
((B*)(char*)a)->x
对后一种情况没有影响,我们仍然以 int
类型的左值结束。在继承的情况下,如果它是多重继承,那么这可能会破坏代码,因为它将 static_cast
变成了 reinterpret_cast
。
关于c++ - 类型和未定义行为之间的指针转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32108642/