2010-05-27 50 views
5

Các codebase tại nơi làm việc có chứa một số mã trông gần như thế này:Làm cách nào để diễn giải dữ liệu nhị phân dưới dạng số nguyên?

#define DATA_LENGTH 64 

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    // ... initialized data buffer 
    return *(u_int32*)data; 
} 

Mã này hoạt động một cách chính xác, nhưng GCC đưa ra cảnh báo sau đây:

warning: dereferencing pointer ‘serialNumber’ does break strict-aliasing rules 

Ai đó có thể giải thích cảnh báo này? Mã này có nguy hiểm không? Làm thế nào nó có thể được cải thiện?

Cập nhật
Với nhờ James McNellis' câu trả lời tôi đã đưa ra các chức năng tiện ích sau:

template<class T, class Data> 
T BinaryCast(const Data & inData) 
{ 
    T ret; 
    std::copy(&inData[0], &inData[0] + sizeof(ret), reinterpret_cast<char*>(&ret)); 
    return ret; 
} 

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    // ... initialized data buffer 
    return BinaryCast<u_int32>(data); 
} 

Hãy đề nghị cải tiến!

+0

Có lẽ phải làm với con trỏ được đúc từ unsigned char * đến u_int32 * nhưng đã lâu rồi kể từ khi tôi đã làm C++. Nếu vậy, vì DATA_LENGTH là bội số chính xác của 32 nên không có vấn đề gì. –

Trả lời

11

Cảnh báo là vì bạn vi phạm strict aliasing rule.

Một cách để làm điều đó một cách chính xác sẽ được sao chép các byte từ bộ đệm data vào một đối tượng u_int32 và trở về đối tượng:

unsigned char data[DATA_LENGTH]; 
// ... initialized data buffer 

u_int32 i; 
assert(sizeof (i) <= DATA_LENGTH); 
std::copy(&data[0], &data[0] + sizeof (i), reinterpret_cast<char*>(&i)); 
return i; 

giải pháp này hoạt động bởi vì trong C++ nó được phép truy cập vào bất kỳ loại đối tượng dưới dạng mảng char.

(std::copy() là trong <algorithm>)

+0

Chính xác những gì tôi muốn đăng. Mặc dù giải pháp của tôi sử dụng memcpy. – PeterK

+1

Thậm chí nếu bạn không sử dụng STL, ít nhất sử dụng reinterpret_cast. – user347594

+0

Một sự tinh tế cần được đề cập bất cứ khi nào nói về (de) serializing dữ liệu theo cách này là của endianness. Nó ít quan tâm hơn bây giờ mà Apple sử dụng chip Intel, nhưng nên được giữ trong tâm trí vì sợ rằng dữ liệu của bạn trở nên bị hỏng nghiêm trọng. – user168715

2

Trong C và C++ ngôn ngữ reinterpreting bộ nhớ bị chiếm đóng bởi đối tượng của một loại như một đối tượng kiểu khác là bất hợp pháp - nó dẫn đến hành vi không xác định. Một số trình biên dịch sử dụng quy tắc này để thực hiện tối ưu hóa liên quan đến răng cưa tích cực. Kết quả là, mã của bạn có thể không hoạt động như mong đợi, nếu bạn thực hiện việc diễn giải lại nói trên.

Trong C/C++, có thể diễn giải lại bất kỳ đối tượng nào dưới dạng mảng char, nhưng không thể lấy một mảng độc lập của char và diễn giải lại là đối tượng của một kiểu khác. Đây là những gì mã của bạn đang làm.

Ngoài các vấn đề về răng cưa, bạn phải lưu ý rằng mảng char tự động độc lập không được đảm bảo được căn chỉnh chính xác để được đọc dưới dạng giá trị u_int32.

Cách thích hợp để làm những gì các mã trên là cố gắng làm là để sao chép các mảng nguồn thành một u_int32 giá trị trung gian sử dụng memcpy

u_int32 SmartKey::SerialNumber() 
{ 
    unsigned char data[DATA_LENGTH]; 
    u_int32 u; 
    // ... 
    memcpy(&u, data, sizeof u); 
    return u; 
} 

Tất nhiên, bạn phải chắc chắn rằng các endianness của dữ liệu giống với độ dài của các đối tượng u_int32 trên nền tảng của bạn.

-1

Không chắc, nhưng tôi nghĩ bạn có thể làm như thế:

return (u_int32)&data; 
+0

Điều đó sẽ chuyển đổi địa chỉ của 'dữ liệu' thành' u_int32'. OP muốn đọc nội dung thực được lưu trữ trong các phần tử đầu tiên của 'dữ liệu'. Thậm chí không điều khiển từ xa. – AnT

0

Tôi nghĩ vấn đề là thực sự ở đâu đó trong mã elided của bạn để khởi tạo dữ liệu [] cấu trúc. Tôi không nghĩ rằng nó có bất cứ điều gì để làm với dàn diễn viên của bạn, đó là tốt.

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