[英]Cast array of bytes to POD
比方說,我有一組無符號字符代表一堆POD對象(例如從套接字或通過mmap讀取)。 它們代表哪些類型以及在運行時確定的位置,但我們假設每個類型已經正確對齊。
將這些字節“轉換”為相應的POD類型的最佳方法是什么?
解決方案應該符合c ++標准(比方說> = c ++ 11)或者至少可以保證使用g ++> = 4.9,clang ++> = 3.5和MSVC> = 2015U3。 編輯:在Linux,Windows上運行x86 / x64或32/64位臂。
理想情況下,我想做這樣的事情:
uint8_t buffer[100]; //filled e.g. from network
switch(buffer[0]) {
case 0: process(*reinterpret_cast<Pod1*>(&buffer[4]); break;
case 1: process(*reinterpret_cast<Pod2*>(&buffer[8+buffer[1]*4]); break;
//...
}
要么
switch(buffer[0]) {
case 0: {
auto* ptr = new(&buffer[4]) Pod1;
process(*ptr);
}break;
case 1: {
auto* ptr = new(&buffer[8+buffer[1]*4]) Pod2;
process(*ptr);
}break;
//...
}
兩者似乎都有效,但兩者都是c ++中的AFAIK未定義行為1) 。 而且只是為了完整性:我知道將通常的東西復制到適當的局部變量中的“通常”解決方案:
Pod1 tmp;
std::copy_n(&buffer[4],sizeof(tmp), reinterpret_cast<uint8_t*>(&tmp));
process(tmp);
在某些情況下,它可能不是其他人的開銷,在某些情況下甚至可能更快,但性能除外,我不再能夠例如修改數據並且說實話:它讓我很生氣,知道我有右位在內存中的適當位置,但我不能使用它們。
我想出的一個有點瘋狂的解決方案是:
template<class T>
T* inplace_cast(uint8_t* data) {
//checks omitted for brevity
T tmp;
std::memmove((uint8_t*)&tmp, data, sizeof(tmp));
auto ptr = new(data) T;
std::memmove(ptr, (uint8_t*)&tmp, sizeof(tmp));
return ptr;
}
g ++和clang ++似乎能夠優化掉那些副本,但我認為這會給優化器帶來很多負擔,並可能導致其他優化失敗,不能用於const uint8_t*
(盡管我不想實際修改它只是看起來很可怕(不要以為你會得到過去的代碼審查)。
1)第一個是UB,因為它打破了嚴格的別名,第二個可能是UB( 這里討論過 ),因為標准只是說生成的對象沒有初始化並且具有不確定的值(而不是保證底層內存不受影響) 。 我相信第一個等效的c代碼是明確定義的,因此編譯器可能允許這與c-header的兼容性,但我不確定這一點。
最正確的方法是創建所需POD類的(臨時)變量,並使用memcpy()
將數據從緩沖區復制到該變量中:
switch(buffer[0]) {
case 0: {
Pod1 var;
std::memcpy(&var, &buffer[4], sizeof var);
process(var);
break;
}
case 1: {
Pod2 var;
std::memcpy(&var, &buffer[8 + buffer[1] * 4], sizeof var);
process(var);
break;
}
//...
}
執行此操作的主要原因是對齊問題:緩沖區中的數據可能無法正確對齊您正在使用的POD類型。 制作副本可以消除這個問題。 即使網絡緩沖區不再可用,它也允許您繼續使用變量。
只有當您完全確定數據已正確對齊時,才能使用您提供的第一個解決方案。
(如果您正在從網絡讀取數據,則應始終首先檢查數據是否有效,並且不會在緩沖區外讀取。例如,使用&buffer[8 + buffer[1] * 4]
,應檢查該地址的開頭加上Pod2的大小是否超過緩沖區長度。幸運的是你正在使用uint8_t
,否則你還必須檢查buffer[1]
是否為負數。)
使用union可以逃避反鋸齒規則。 事實上,這就是工會的意義所在。 因此,在C ++標准(條款3.10.10.6)中明確允許從屬於聯合的類型的聯合類型轉換指針。 C標准(6.5.7)允許相同的內容。
因此,根據其他屬性,樣品的符合等效值可以如下所示。
union to_pod {
uint8_t buffer[100];
Pod1 pod1;
Pod1 pod2;
//...
};
uint8_t buffer[100]; //filled e.g. from network
switch(buffer[0]) {
case 0: process(reinterpret_cast<to_pod*>(buffer + 4)->pod1); break;
case 1: process(reinterpret_cast<to_pod*>(buffer + 8 + buffer[1]*4)->pod2); break;
//...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.