2015-12-11 13 views
7

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_GETREGSETaddr==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?

+1

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

+0

* 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

+0

Đặ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 ở đó. –

Trả lời

4

Bằng cách không đọc tệp dưới dạng luồng byte, nhưng dưới dạng luồng của cấu trúc Data.

Chỉ cần thực hiện ví dụ:

Data data; 
file.read(reinterpret_cast<char*>(&data), sizeof(data)); 
+0

Vui lòng xem chỉnh sửa câu hỏi. – Ruslan

1

tôi nghĩ này là một ngoại lệ đặc biệt để các quy tắc nghiêm ngặt răng cưa cho tất cả các loại char (ký, unsigned, và đồng bằng). Vì vậy, tôi nghĩ rằng tất cả các bạn phải làm là thay đổi chữ ký của magic_reinterpret tới:

Data* magic_reinterpret(char *raw) 

Không làm việc tôi sợ. Theo nhận xét của deviantfan, bạn có thể đọc (hoặc viết) Data dưới dạng một loạt các [unsigned] char, nhưng bạn không thể đọc hoặc viết char dưới dạng Data. Câu trả lời của Joachim là chính xác.

Đã nói tất cả điều đó. Nếu bạn đang đọc từ mạng hoặc tệp, chi phí bổ sung khi đọc đầu vào của bạn dưới dạng chuỗi octet và tính toán các trường từ bộ đệm sẽ không đáng kể (và sẽ cho phép bạn đối phó với những thay đổi trong bố cục giữa các phiên bản trình biên dịch và máy móc).

+0

Trong khi 'char' thực sự là đặc biệt, nó không giống như bạn nghĩ. Nó không phải là hai chiều. Btw., 'Signed char' là không ok, ngay cả khi đồng bằng' char' hoạt động như biến ký. – deviantfan

+0

Chết tiệt! Tôi đã trả lời 4 phút trước, quay trở lại để sửa chữa nó, và tìm thấy ai đó đã bình luận. –

+0

Bạn có muốn nó nhiều hơn nếu bạn thu thập những lời bình tĩnh im lặng, mà không biết có gì sai? ... SO có khá nhiều người dùng hoạt động, trong các thẻ như C++ không mất nhiều thời gian để nhận phản hồi. – deviantfan

Các vấn đề liên quan