在指针和引用上使用“reinterpret_cast”时编译器警告的差异[关闭]

问题描述 投票:0回答:1

假设我有两个结构

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);

https://godbolt.org/z/4o7bhGrYP

c++ reinterpret-cast
1个回答
3
投票

此处适用的

reinterpret_cast
的项目符号是:

  1. 任何对象指针类型 T1* 都可以转换为另一个对象指针类型 cv T2*。这完全等同于
    static_cast<cv T2*>(static_cast<cv void*>(expression))
    (这意味着如果 T2 的对齐要求不比 T1 的更严格,则指针的值不会改变,并且将结果指针转换回其原始类型会产生原始值)。在任何情况下,只有在类型别名规则允许的情况下,才能安全地取消引用结果指针(见下文)

  1. 类型 T1 的左值 (C++11 前) 泛左值 (C++11 起) 表达式可以转换为对另一个类型 T2 的引用。结果是
    *reinterpret_cast<T2*>(p)
    的结果,其中 p 是指向由表达式指定的对象的“指向 T1 的指针”类型的指针。没有创建临时文件,没有复制,没有调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问生成的引用(见下文)

类型别名规则说

每当尝试通过 AliasedType 类型的泛左值读取或修改 DynamicType 类型对象的存储值时,行为是未定义的,除非满足以下条件之一:

唯一可能适用的项目符号是:

  • AliasedType 和 DynamicType 类似。

类似的是以下之一:

  • 它们是同一类型;或
  • 都是指针,指向的类型相似;或
  • 都是指向同一类成员的指针,指向的成员类型相似;或
  • 都是大小相同的数组或者都是未知界的数组,数组元素类型相似。 (直到 C++20)
  • 它们都是相同大小的数组或者其中至少一个是未知边界数组,并且数组元素类型相似。

A
C
不是数组。
A*
C*
不相似。

因此,

c
c1
都没什么用。
c
无法访问,
c1
只能转换回
A*
。其他任何事情都会导致未定义的行为。

编译器没有义务对未定义的行为发出警告或错误。它会针对引用发出警告,因为您无法对该引用执行任何操作。它不会对指针发出警告,因为这样的转换可能很有用(但只是作为中间结果,只能转换回

B*
)。

reinterpret_cast
不是你可以仅仅因为
A
C
看起来一样就可以应用的任意转换。如果您想将
B
转换为
C
,您需要进行适当的转换。

© www.soinside.com 2019 - 2024. All rights reserved.