reinterpret_cast从char*到uint32_t*在CPP中是未定义的行为吗?

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

我从事一个中型开源项目,该项目需要将原始字节解释为不同类型。这是通过创造性地使用重新解释铸造来实现的。然而,在一个简单的测试用例中,使用 GCC 10 或更高版本进行编译且优化级别高于 O1 时,测试会失败。较低版本的 GCC 和 Clang 不会显示此问题。

我们成功编写了一个可重复的小样本,如下所示。这需要一个 uint64_t 并返回一个指向包含 uint64_t 数据的 uint32_t 数组的指针。预期结果是 bar() 以 uint32_t 形式返回数字 1。

#include <cstddef>
#include <cstdint>

struct RegisterValue {
    size_t bytes = 8;
    alignas(8) char localValue[16] = {42};
    void set(uint64_t value) {
        uint64_t* view = reinterpret_cast<uint64_t*>(this->localValue);
        view[0] = value;
    }
    uint32_t* getAsVector() {
        if (bytes <= 16) {
            return reinterpret_cast<uint32_t*>(this->localValue);
        } else {
            static uint32_t t = {0xdeadbeef};
            return &t;
        }
    }
};
extern "C" uint32_t bar() {
    RegisterValue v;
    v.set(0x0000000200000001);
    return v.getAsVector()[0];
}

您可以在此处使用编译器资源管理器中的代码:https://godbolt.org/z/1eYs4orKc

本质上,所有的reinterpret_casting都被GCC积极优化,导致汇编只返回一个立即数。这是预料之中的。然而,在较低的优化级别 (O1) 下,这会返回预期结果 (1),但在较高的优化级别 (O2、O3) 下,这会错误地返回 42。 使这更加令人困惑的是几乎对源的任何更改(例如删除 if/否则将第一个返回保留在 getAsVector 中)会导致预期的行为。

这听起来像 UB,但我们已经阅读了 https://en.cppreference.com/w/cpp/language/reinterpret_cast 并且不能自信地说这是否是 UB?如果是,那么我们需要更新我们的实现,如果不是,这将表明存在编译器错误。

任何见解都会有帮助。

c++ compiler-optimization undefined-behavior reinterpret-cast
1个回答
0
投票

是的,您所做的是未定义行为,类型别名冲突,因为您通过

localValue
访问您的
uint64_t*
。根据 reinterpret_cast
uint64_t*
可以通过
char*
进行类型访问,但反之则不然。第一部分可以用
std::memcpy
:

修复
void set(uint64_t value) {
    std::memcpy(localValue, &value, sizeof(value));
}

但是如果不引入额外的类字段来保存结果,我对

uint32_t* getAsVector()
没有任何直接的想法。您可以返回
const char*
并让用户处理
uint32
转换,或者返回
std::vector<std::uint32_t>

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