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