Giả sử chúng tôi lấy một số dữ liệu dưới dạng chuỗi byte và muốn diễn giải lại chuỗi đó dưới dạng cấu trúc (có một số đảm bảo rằng dữ liệu thực sự ở định dạng chính xác). Ví dụ:Làm cách nào để diễn giải lại chuỗi các byte dưới dạng cấu trúc POD mà không gây ra UB?
#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";
}
Bây giờ, magic_reinterpret
đây thực sự là xấu, vì nó phá vỡ quy tắc bí danh nghiêm ngặt và do đó gây ra UB.
Làm cách nào để thay thế được thực hiện để không gây ra UB và không thực hiện bất kỳ bản sao dữ liệu nào như với memcpy
?
EDIT: các getDataBytes()
chức năng trên được trên thực tế coi một số chức năng không thể thay đổi. Ví dụ thế giới thực là ptrace(2)
, trên Linux, khi request==PTRACE_GETREGSET
và addr==NT_PRSTATUS
, viết (trên x86-64) một trong hai cấu trúc có thể có các kích thước khác nhau, tùy thuộc vào độ bit của tracee và trả về kích thước. Ở đây ptrace
mã gọi không thể dự đoán loại cấu trúc nào nó sẽ nhận được cho đến khi thực sự thực hiện cuộc gọi. Làm thế nào nó có thể sau đó một cách an toàn reinterpret kết quả nó được như là loại con trỏ chính xác?
Về cơ bản, thay vì đúc một mảng byte với dữ liệu vào cấu trúc, lấy một cá thể struct, truyền nó (địa chỉ) vào một mảng byte và điền mảng này với dữ liệu. [J. Pileborg đăng nó như mã dưới đây] – deviantfan
* Nhưng *, trong khi bạn đã đề cập đến nó cho mình, chỉ cần một lời nhắc nhở để luôn luôn nghĩ về kích thước int, định dạng số âm, liên kết cấu trúc, đệm, ... – deviantfan
Đặc biệt, dữ liệu sẽ là 64 bit trên hầu hết các máy, nhưng chỉ có 56 bit thông tin ở đó. –