假设我们将一些数据作为一个字节序列获得,并希望将该序列重新解释为一个结构(有一些保证数据确实是正确的格式).例如:
#include#include #include #include #include struct Data { std::int32_t someDword[629835]; std::uint16_t someWord[9845]; std::int8_t someSignedByte; }; Data* magic_reinterpret(void* raw) { return reinterpret_cast(raw); // BAD! Breaks strict aliasing rules! } std::vector getDataBytes() { std::ifstream file("file.bin",std::ios_base::binary); if(!file) std::abort(); std::vector rawData(sizeof(Data)); file.read(rawData.data(),sizeof(Data)); if(!file) std::abort(); return rawData; } int main() { auto rawData=getDataBytes(); Data* data=magic_reinterpret(rawData.data()); std::cout << "someWord[346]=" << data->someWord[346] << "\n"; data->someDword[390875]=23235; std::cout << "someDword=" << data->someDword << "\n"; }
现在magic_reinterpret
这里实际上是坏的,因为它打破了严格的别名规则,从而导致UB.
它应该如何实现不会导致UB而不执行任何数据副本memcpy
?
编辑:getDataBytes()
上面的函数实际上被认为是一些不可改变的功能.一个真实的例子是ptrace(2)
,在Linux上,何时request==PTRACE_GETREGSET
和addr==NT_PRSTATUS
(根据x86-64)写入两种不同大小的结构中的一种,具体取决于跟踪位数,并返回大小.这里ptrace
调用代码无法预测它实际执行调用之前将获得的结构类型.那怎么能安全地重新解释它得到的结果作为正确的指针类型?