假设我有两个结构
struct B
{
int n;
};
struct C
{
int k;
};
和
B b = {};
C& c = reinterpret_cast<C&>(b); //Not Ok , compiler(gcc 8.5 with -O2 -Wall) is not happy
C *c1 = reinterpret_cast<C*>(&b); //Okay, compiler(gcc 8.5 with -O2 -Wall) is happy
示例代码:
#include<new>
#include<iostream>
int main() {
struct B { int n; };
struct C
{ int k; }
;
B b = {};
C c = reinterpret_cast<C&>(b);
C *c1 = reinterpret_cast<C*>(&b);
printf("b.n is %d\n",b.n);
printf("c.k is %d\n",c.k);
printf("c1.k is %d\n",c1->k);
return 0;
}
有人可以帮助我理解为什么上述代码的行为存在差异,尽管我相信它们在功能上是相同的吗?
我希望编译器即使在引用时也会很高兴,因为我知道这两种类型具有相同的内存对齐方式。
我收到以下警告
<source>:142:29: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
C c = reinterpret_cast<C&>(b);
reinterpret_cast
的项目符号是:
- 任何对象指针类型 T1* 都可以转换为另一个对象指针类型 cv T2*。这完全等同于
(这意味着如果 T2 的对齐要求不比 T1 的更严格,则指针的值不会改变,并且将结果指针转换回其原始类型会产生原始值)。在任何情况下,只有在类型别名规则允许的情况下,才能安全地取消引用结果指针(见下文)static_cast<cv T2*>(static_cast<cv void*>(expression))
和
- 类型 T1 的左值 (C++11 前) 泛左值 (C++11 起) 表达式可以转换为对另一个类型 T2 的引用。结果是
的结果,其中 p 是指向由表达式指定的对象的“指向 T1 的指针”类型的指针。没有创建临时文件,没有复制,没有调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问生成的引用(见下文)*reinterpret_cast<T2*>(p)
类型别名规则说
每当尝试通过 AliasedType 类型的泛左值读取或修改 DynamicType 类型对象的存储值时,行为是未定义的,除非满足以下条件之一:
唯一可能适用的项目符号是:
- AliasedType 和 DynamicType 类似。
类似的是以下之一:
- 它们是同一类型;或
- 都是指针,指向的类型相似;或
- 都是指向同一类成员的指针,指向的成员类型相似;或
- 都是大小相同的数组或者都是未知界的数组,数组元素类型相似。 (直到 C++20)
- 它们都是相同大小的数组或者其中至少一个是未知边界数组,并且数组元素类型相似。
A
和 C
不是数组。 A*
和 C*
不相似。
因此,
c
和 c1
都没什么用。 c
无法访问,c1
只能转换回 A*
。其他任何事情都会导致未定义的行为。
编译器没有义务对未定义的行为发出警告或错误。它会针对引用发出警告,因为您无法对该引用执行任何操作。它不会对指针发出警告,因为这样的转换可能很有用(但只是作为中间结果,只能转换回
B*
)。
reinterpret_cast
不是你可以仅仅因为 A
和 C
看起来一样就可以应用的任意转换。如果您想将 B
转换为 C
,您需要进行适当的转换。