2015-11-04 126 views
1

在我們公司中,通過網絡發送C/C++結構非常普遍。任何結構具有一個或多個uint32_t的字段:通過網絡發送結構

typedef struct { 
    uint32_t data1; 
    uint32_t data2; 
} Data; 

和字段(數據1,數據2,...)封裝,我們需要發送的真實數據。我們的數據大部分是單個或幾個比特長,所以爲了節省空間/帶寬,它們在特定比特位置的這些字段內對齊。

要訪問我們的真實數據,我們編寫了「干將」和「二傳手宏與bitshifting和位掩碼(struct的外面!):

#define READY_MASK 0x01  // indicates that READY is a single bit value 
#define READY_OFFSET 3  // indicates position of READY inside 32-bit field 
#define IS_READY(x) { ... } // returns READY value from x field 
#define SET_READY(x,r) { ... } // sets READY value to x field 

現在我想修改,簡化和使這個過程更安全通過添加getter和setter直接進入結構,例如:

typedef struct { 
    uint32_t data1; 

    #define READY_MASK 0x01 
    #define READY_OFFSET 3 
    inline void set_ready(uint32_t r) { /*...*/ } 
    inline uint32_t is_ready() { /*...*/ } 
    // lots of other getters and setters 
} Data1; 

至於我的實驗是正確的,我已經注意到,這種修改不以大小的影響結構sizeof(Data)==sizeof(Data1),我可以發送這個結構網絡以及另一端的接收和解碼。

我的問題是:在這個修改中有什麼不正確的嗎?有什麼危險的或我應該知道的任何事情?

+2

那'Data1'結構不是C結構,C結構不能有成員函數。你確定你不用C++編程嗎? –

+0

假設它是C++(因爲它只是普通的不會在C編譯...),那麼如何將函數添加到結構使事情更安全? –

+0

當然@JoachimPileborg,Data1是C++ – crooveck

回答

1

原則上,如果不添加虛擬函數,將不會有vptr和struct size與沒有成員函數的情況相同。但它不再是一個POD結構,所以你可以很容易地進入UB的未定義行爲的黃昏區域。由於C++還沒有標準化的ABI,所以通常採用C型POD結構是很好的。通過網絡進行序列化。爲C++模塊/子系統製作精簡的可移植API也同樣適用於C ABI。另外,如果你考慮這些(如一些評論者的建議),我建議你遠離位域,手動位掩通常更容易分析和製作便攜。比特場可以例如給你排序問題,這至少部分是因爲結構'始終'的第一個成員結束於struct start的較低內存地址,而不管字節順序如何。位域如何工作還可能取決於傳輸介質,例如, BSD網絡流套接字是一個字節流,但許多硬件接口複製整個32位字大小的塊而不是字節。

2

您所做的修改不會造成任何傷害,但它也不會產生任何效果,因爲無論如何struct結構都是公共的。之所以Data和Data1是相同的大小,是因爲你添加的是內聯函數,它不佔用對象中的空間,而是用實際函數代碼替換任何函數調用。現在

,如果您是通過網絡發送結構和二進制數據,必須考慮以下兩個原則:

  • 按照慣例整數使用網絡字節順序是相對於86的小大端發送尾段。如果您確定所有發送和接收數據的機器都是x86,則這不是問題。但是,如果任何機器具有不同的排列順序,如Big Endian或No Endian(或Middle Endian),則會出現問題。例如,你可以有一個ARM處理器,SPARC等在C你有以下宏:

    還有ntohs,htons,再用ntohl,htonl

  • 你還必須考慮不同的記憶比對。內存對齊的結構取決於體系結構,編譯器和編譯模式。編譯器可能會添加填充(它們不能在C中重新排序成員,但請閱讀:Can a C++ compiler re-order elements in a struct)。此外,像32位和64位體系結構這樣的類型在長度上有不同的大小。即使可以肯定,只有兩個unsigned int成員不會有問題,但您絕對不應該從結構體向存儲器副本發送消息中發送的數據緩衝區,也不應將消息中的原始數據從存儲器副本複製到一個結構。基本上,你不應該這樣做:

    字符緩衝區[BUF_SIZE]

    Data1 myData;

    的memcpy(緩衝液,myData的,的sizeof(數據));

https://en.wikipedia.org/wiki/Data_structure_alignment

你應該將會員緩衝一個接一個。從消息中收到的原始數據填充結構時也是如此。

我也建議不要使用位域

,讀Why bit endianness is an issue in bitfields?。但要非常清楚,在你的片段中,你不使用位域。使用位域與使用整數中的位來表示某些特定狀態不同。在這種情況下,您做得很好,應該只考慮具有不同字節順序的體系結構中的整數表示(如果這是可能的情況)。