2012-04-19 124 views
0
構件之間間隙

我有這樣一個結構:無效的sizeof()結構,

typedef struct _HEADER_IO 
    { 
     uint8_t field1 : 2; 
     uint8_t field2 : 4; 
     uint8_t field3 : 1; 
     uint8_t field4 : 1; 
     uint16_t field5; 
     uint8_t field6; 
    } HEADER_IO; 

這basicly消息報頭,將通過TCP發送。服務器讀取它以便它知道緩衝區中的數據。然而,由於某種原因,大小爲4字節(2 + 4 + 1 + 1第一字節+來自字段5 + 2字節字段6的2字節)的大小爲6字節。

看它在內存觀點是:

XX AA XX XX XX AA 

相反的:

XX XX XX XX 

凡AA不會被置不管我做什麼。這是一個問題,因爲我正在計劃將標頭設置爲send()以連接到服務器,並且包含額外的字節使服務器解釋標頭錯誤。我究竟做錯了什麼?

回答

0

在標準C中,你不能幫助結構成員可以在它們之間插入填充的事實。您必須編寫一個函數來解碼數據並在處理之前將其存儲在您的結構中。這是因爲在某些體系結構中,未對齊的內存訪問(從指針讀取不與4字節對齊)非常昂貴,並且C會自動填充結構以避免成本。沒有標準的方式來打開或關閉功能。

例如,在海灣合作委員會可以在結構定義之後添加__attribute__((packed))和Visual Studio有一些#pragma命令(見http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html),其也由GCC支持,但要注意的是整體,這是不規範的。

由於您的評論中提到這是一個Windows程序,也許它會工作,如果你的結構定義之前,補充一點:

#pragma pack(push,1) 

這之後:

#pragma pack(pop) 

雖然它會更可移植性來編寫代碼來更多地手動解碼頭部,上面的方法應該會更快。

+0

你是什麼意思'寫一個函數來解碼數據並在處理之前將它存儲在你的結構中' – 2012-04-19 13:04:31

+0

我很抱歉,你讓我困惑,數據數組來自哪裏,爲什麼你將24位而不是8? – 2012-04-19 13:09:13

+0

但是,如果您不關心可移植性,則可以使用編譯器指令(如GCC或Visual C++專用指令)來設置結構打包。然後它會按原樣工作。 – jjrv 2012-04-19 13:09:22

1

一般來說,對這些事情使用位域是一個壞主意。既然你事先不知道哪一個字節的位最終會出現,並且有填充和對齊的問題。

在我看來,最好是「自己動手」,因爲你需要更多的控制外部表示,而不是C結構給你的東西,並手動完成。您當然可以將結構保存爲內存中(內部)表示。

基本上,你會寫像一個函數:

size_t header_serialize(unsigned char *buf, size_t max, const HEADER_IO *header); 

,他們的工作將是,在內存buf,建立代表header正確的字節序列。

爲了澄清(基於評論),其目的是從header中讀取字段,而不僅僅是例如

memcpy(buf, header, sizeof *header); /* DON'T DO THIS! */ 

相反,你應該用字節來組裝預期的外在表徵,字節,從header領域。這樣,無論編譯器如何處理內存中的格式header,您都可以獲得相同的外部表示。

+0

有一個問題,保證結構的對齊和填充總是相同的,或者會因系統的不同而不同,從而導致我的序列化函數不一致 – 2012-04-19 13:23:20

+2

對齊和填充因系統而異,這就是爲什麼您的序列化函數應該訪問該結構以標準方式讀取和寫入其成員,以便編譯器處理它。然後在序列化的時候,你寫入一個無符號字符的緩衝區,以便控制確切的內容。 – jjrv 2012-04-19 13:26:40

+0

很高興知道謝謝。 +1這個選擇,將記住它 – 2012-04-19 13:38:10