字節對齊與字節序

整理下字節對齊和字節序的相關問題,原先整理的都丟了。。。

一、字節對齊

字節對齊是什麼?當在c語言中定義複合數據類型,如數組,枚舉,聯合,結構體等,類型中的成員往往不止一個,而數據定義好之後,數據在內存中的排列就是如定義的那般,一個成員挨着一個成員,連續存放的嗎?
答案是否定的。複合數據類型在內存中存儲時,成員之間可能存在一些空洞(又名padding),你可以用sizeof查看一些結構,計算原來成員總和,有些結構的實際大小就比總和大一些。在一些對數據排列要求嚴格的場景,如網絡上傳輸的報文,因其內在定義都是嚴格規定的,若還是存在空洞,那麼數據肯定就出錯了。這時一般使用#pragma pack (1),避免空洞出現。
那麼爲什麼會有空洞存在呢?
空洞的存在應該是一種用空間換時間的手段,這要涉及到對內存中數據的訪問方式,爲了提高cpu讀取效率,就出現了字節對齊:如果不是字節對齊的,本來一次可以取完的數據可能需要取兩次。
32位cpu上默認的指定對齊值是4字節,64位cpu上默認的指定對齊值是8字節。
那麼字節對齊是如何對齊的?默認對齊值有什麼用?指定的對齊值又有什麼用?
這裏有幾個概念
默認對齊值:基本類型自身默認對齊值,爲自身大小。複合類型默認對齊值:所有成員中自身對齊值最大的那個成員的對齊值(聯合和結構體是成員中最大對齊值成員的對齊值)
指定對齊值:#pragma pack(n) 若沒指定,那麼這裏看平臺的默認指定對齊值
有效對齊值:取指定對齊值(沒指定則爲平臺默認指定對齊值)和自身默認對齊值小的那個。

另外複合類型除了保證其成員對齊所加的padding外,還有可能在其末尾添加padding,用來保證結構體大小是其有效對齊值的整數倍。這個是爲了複合類型數組所考慮的,數組無對齊這一說,所以假如結構體末尾不填充,結構體數組的第二個成員就不能對齊了。結構體的地址其實是對所有類型都滿足對齊的,所以就只需要滿足結構體中成員的偏移量是有效對齊值的整數倍就可以了。

之前遇到過一個問題,我想用#pragma pack(1)來消除空洞,用來避免顯式聲明padding,然後別人告訴我最好不要用#pragma pack(1),說可能會出問題,我後來搜了一下才知道這個不能隨便用,這裏引用一下別人的描述:

各個硬件平臺對存儲空間的處理上有很大的不同,一些平臺對某些特定類型的數據只能從某些特定地址開始存取。比如有些架構的CPU在訪問一個沒有進行對齊的變量的時候會發生錯誤,那麼在這種架構下編程必須保證字節對齊。比如sparc系統,如果取未對齊的數據會發生錯誤,舉個例:

char ch[8]; char *p = &ch[1]; int i = *(int *)p; 運行時會報segment
error,而在x86上就不會出現錯誤,只是效率下降。
————————————————
版權聲明:本文爲CSDN博主「OopspoO」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/cclethe/article/details/79659590

二、字節序

字節序,即超過1字節的其他基本類型的字節排列順序,比如short in long等,他們的數據在內存中存放的空間超過一字節,排列順序就是字節序。這裏有兩種排列順序,即數據高位存放在內存中的低字節(網絡序/大端),和數據低位存放在內存中的低字節(主機序/小端)。
比如一個int類型的數據,它的值爲0x01020304,在不同的cpu架構上,它在內存中存儲的順序可能是不同的。在大端設備上爲:
內存地址遞增方向->

0x1|0x2|0x3|0x4

而在小端設備上:

0x4|0x3|0x2|0x1

一般來說,爲了字節序不同的設備間通信的兼容,數據在發送前都要轉換爲網絡序,在接收後按需決定是否需要轉換。大端設備發送接收都不需要轉換字節序,而小端設備發送接收都要轉換。而主機轉網絡和網絡轉主機序的函數其實作用是相同的。
超過一字節的數據在涉及通信(或存儲)時,爲了保證一致性,都需要轉字節序。字符串數組不需要轉字節序,而別的比如int型數組則需要轉字節序。
舉例,比如接收並存儲uchar szxxx[3]和int aixxx[3],在內存中,存儲後是這樣的。
內存地址遞增方向->
發送前(遠端設備 大端)

uchar szxxx[3] = {1,2,3};
0x1|0x2|0x3|

int aixxx[3] = {1,2,3};
0x1|0x0|0x0|0x0 0x2|0x0|0x0|0x0 0x3|0x0|0x0|0x0

接收後(本端設備 小端)
uchar szxxx[3] = {1,2,3};
0x1|0x2|0x3|

int aixxx[3] = {1,2,3};
0x1|0x0|0x0|0x0 0x2|0x0|0x0|0x0 0x3|0x0|0x0|0x0

轉字節序後(本端設備 小端)
uchar szxxx[3] = {1,2,3};
0x1|0x2|0x3|

int aixxx[3] = {1,2,3};
0x0|0x0|0x0|0x1 0x0|0x0|0x0|0x2 0x0|0x0|0x0|0x3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章