C中結構體和字節流的互換及內存對齊


 

mystruct p;
char buff[50];
memcpy(buff,&p,sizeof(p)); //把p的內容以字節形式拷貝到buff中
mystruct* q;
q = (mystruct*)buff; //把buff的內容轉換為結構體mystruct

 

或者

(將字節流轉化成結構體)

msg_header_t msgRecved;

num = data_recv(fd,(void *)buf,sizeof(msg_header_t));

memcpy(&msgRecved,buf,sizeof(msg_header_t));

 

經常需要在程序中將遠程傳來的字節流進行結構化,或者將結構化的數據變成字節流傳給遠程主機。

在C/C++程序中,結構化數據通常用結構體來組織,結構體也能夠方便的轉換為字節流,因此結構化的數據傳輸本不該成為問題,

但在VC或GCC的默認設置下編譯出的程序,卻有一個值得注意的問題——結構體的對齊。    

結構體的對齊是編譯器為加快程序運行,在結構體中填入一些空白字節,是的結構體成員按一定規則對齊。

例如結構: 
struct MyStruct 
{     
char a;     
int  b; 
};
   
     按一般計算其類洗過那大小應為5個字節,但在上述編譯器默認設置下,使用sizeof計算出的結構體類型大小為8個字節,因為編譯器在該結構的第一個成員后填入了3個字節來保持成員的對齊。這樣,在序列化字節流的時候就產生了問題,該結構類型的變量指針直接轉化成char *或者使用memcpy得出的字節流即為8個字節而非5個字節。一般情況下,傳輸規約只定義有效字節,因而上述方式產生的字節流中3個字節將不在規約中定義,因此遠程主機在解析的時候會產生誤解,當然除非遠程解析程序采用同樣的序列化方式和同樣的結構對齊設置,而這在某些情況下難以保證。所以,在結構的序列化過程中,必須僅處理有效字節,使得字節流符合規約定義。
    多余字節的產生原因在於編譯器對結構體成員對齊字節數的設置的不同,VC和GCC(MinGW)的默認設置為8字節對齊,所以需要改變其默認的對齊字節數為按單字節對齊,即可以解決上述問題。具體方法是在代碼頭中加入: 
   #pragma pack(1)即可。
   此后使用sizeof計算出的結構體大小即為各成員大小的總和。這在按既定規約編寫網絡二進制數據傳輸程序時需要注意;

 

 

內存對齊的問題:
重要的是,數據是否有拿對。
自然對齊,是變量的內存地址,正好是它類型長度的整數倍;
 
對於結構體,
若采用
 
struct std{
        char data[9];
        int person;
        } __attribute__((aligned (1)));
 
        struct std my_std;
 
        printf("size of my student = %d\n",sizeof(my_std));    ===> 結果是16
 
struct std1{
        char data[9];
        int person;
        } __attribute__((packed));
 
        struct std1 my_std1;
 
        printf("size of my student1 = %d\n",sizeof(my_std1));===> 結果是13
 
若沒有 __attribute__((packed)),則按照四字節對齊,
 
舉例
std結構體中data 為4字節對齊的字節流數據;
 
int len;
len = *(int *)&data[7];
7非4的整數倍,就會出現拿不到想要的數據問題;
 
data[4] data[5] data[6] data[7] 用上述方法拿到的數據一樣。
前面的*(int*) 相當與一個“耦煤器“ ,它不會從地址對齊的當前塊跨越到下一塊。
 
 
修改成:
char len[4];
memset(len,0,sizeof(len));
memcpy(len,&data[7],sizeof(len));
則能拿到想要的數據;
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM