C++:reinterpret_cast的奇怪行为

C++: odd behavior with reinterpret_cast

本文关键字:cast reinterpret C++      更新时间:2023-10-16

我有以下代码片段

#include <iostream>
using namespace std;
class foobar
{
    public:
    unsigned int a = 97;
    unsigned int b = 98;
    unsigned int c = 99;
};

class barfoo
{
    public:
    char a:32,b:32,c;
};

int main(){
    foobar * f = new foobar();
    barfoo * b = (reinterpret_cast<barfoo *>(f));
    cout << b->a << "-" << b->b << "-" << b->c;
}

输出:

a-b-c

但是,如果我在 barfoo 中指定 c 的宽度,它不起作用(所有值均为 0)。观察

#include <iostream>
using namespace std;
class foobar
{
    public:
    unsigned int a = 97;
    unsigned int b = 98;
    unsigned int c = 99;
};

class barfoo
{
    public:
    char a:32,b:32,c:32;
};

int main(){
    foobar * f = new foobar();
    barfoo * b = (reinterpret_cast<barfoo *>(f));
    cout << b->a << "-" << b->b << "-" << b->c;
}

输出:

--

字符均为 0

知道为什么会这样吗?

链接到 ideone 第一个片段 - 正在工作

Linke to ideone second snippet -- 不起作用

编译器是 gcc-5.1谢谢!!

reinterpret_cast不满足已定义行为的要求,因此,虽然其结果的分配是可以接受的,但b的任何后续访问都是未定义的行为:

barfoo * b = (reinterpret_cast<barfoo *>(f));

reinterpret_cast必须满足以下条件之一,其中foobar是动态类型,barfoo是别名类型:

  • AliasedType 是(可能符合 cv 标准的)DynamicType
  • AliasedType 和 DynamicType 都是指向同一类型 T 的指针(可能是多级的,可能是每个级别的 cv 合格)。
  • AliasedType 是 DynamicType 的(可能符合 cv 标准的)有符号或无符号变体
  • AliasedType 是一种聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归的子聚合元素和所包含联合的非静态数据成员):这使得在给定指向其非静态成员或元素的指针的情况下,可以安全地获取指向结构或联合的可用指针。
  • AliasedType 是 DynamicType 的基类(可能符合 cv 标准)
  • AliasedType 是charunsigned char:这允许检查任何对象的对象表示为无符号字符数组。

由于 AliasedType 不属于任何这些条件,因此访问reinterpret_cast的结果是未定义的行为。

首先,检查 2 个类的大小是否相同。可以想象,填充和对齐可能会使位域 a、b 和 c 的位置与普通成员 a、b 和 c 等的位置不同。为什么要对 32 位位字段使用 char?我的编译器警告位域超过类型大小。

否则(如果您将 char 更改为 barfoo 类的无符号 int),无论向您抛出任何标准 UB ,reinterpret_cast和访问都有很大的机会工作。如果您通过联合使用得到充分支持和完善的类型双关语的成语,则可能更是如此。

通过联合进行类型双关是一种告诉编译器"这两个不同类型的指针可能会别名,不要进行激进的优化,假设它们可能不会别名"的一种方式。