結構體4位元組對齊規則的詳解

2021-06-13 18:24:50 字數 3634 閱讀 4112

一 四位元組對齊的規則

c++中結構體變數的儲存為什麼有個4位元組對齊的規則,這裡是假設32位機器上,cpu在讀取記憶體資料的時候4位元組對齊會取得更快的速度;這是因為:1位元組8位,4位元組正好32位,而32位機器的暫存器,位址什麼的都是32位的,正好一次處理就完成。

例如,下面的結構各成員空間分配情況:

[cpp]view plain

copy

struct

test  

;  

「結構的第乙個成員x1,其偏移位址為0,佔據了第1個位元組。第二個成員x2為short型別,其起始位址必須2位元組對界,因此,編譯器在x2和x1之間填充了乙個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然對界位址上,在它們前面不需要額外的填充位元組。在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。更改c編譯器的預設位元組對齊方式。」

1、這個意思就是說前兩個成員各佔2位元組,後兩個成員各佔4個位元組,換句話說,每個成員佔的位元組數都和鄰近它的前乙個成員型別相關。

2、__attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際占用位元組數進行對齊。用這種方式做的缺點是什麼呢?是不是因為以前計算資源有限,所以要通過這種位元組對齊來節省資源,但現在硬體資源充足了,因此不用過多考慮這個問題?

3、#pragma pack(1) // 按照1位元組方式進行對齊

[cpp]view plain

copy

struct

tcpheader  

;  

[cpp]view plain

copy

#pragma pack() // 取消1位元組對齊方式

這段code表明所有的成員都按照1位元組對齊?那像short型別,只取第乙個位元組的值,後三個忽略?

4、每種資料型別是低位在前,還是高位在前呢?如何判斷?根據os或者其它的條件?

位元組對齊在不同編譯器下語法是不一樣的,在gcc中是#pragma push(1)  #pragma pack(); 在ms c++中用vc的**項裡可以調整,預設是8位元組;

[cpp]view plain

copy

typedef

struct

test;  

位元組對齊,是對齊,比如說char 與 int 如果是4位元組對齊,那麼char也會占用4個位元組,總共佔8位元組,而且結構體物件儲存是按照順序存的,char 肯定在int前面。第二種情況如果1位元組對齊,意味著char只佔1位元組,而結下來int會占用4位元組,這個n位元組對齊的意思是,每個成員占用空間必須是n位元組的倍數,不足n位元組的占用n位元組。那麼以1位元組對齊那它占用5個位元組。

還有每種資料是低位還是高位在前,這個根處理器有關,intel處理是小端對齊,比如說乙個整數522387969用16進製表示是:0x1f 23 02 01,在intel處理器中表示是0x01 02 23 1f,所以在記憶體用0x01 02 03 1f來示522387969,這就是所謂有小端對齊;但在arm處理器中522387969表示是0x1f 23 02 01,這就是所謂的大端對齊,這種方式又叫作網路位元組序。

三 實踐例子分析

例子1:

[cpp]view plain

copy

struct

a  ;  

struct

b  ;  

struct

c  ;  

bool

t;  

printf("sizeof(bool) = %d,sizeof(a) = %d,sizeof(b) = %d,sizeof(c) = %d\n"

,sizeof

(t),

sizeof

(a),

sizeof

(b),

sizeof

(c));  

答案為:1,12,8,12

例子2:

[cpp]view plain

copy

struct

s1  

;  struct

s2  

;  struct

s3  

;  printf("sizeof(s1) = %d,sizeof(s2) = %d,sizeof(s3) = %d\n"

,sizeof

(s1),

sizeof

(s2),

sizeof

(s3));  

答案為:8,12,8

四 相關分析

現代計算機中記憶體空間都是按照byte

劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特 定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊。

各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。比如有些架構的cpu

在訪問 乙個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊

.其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對 資料存放進行對齊,會在訪問效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果乙個

int型(假設為

32位系統)如果存放在偶位址開始的地方,那 麼乙個讀週期就可以讀出這

32bit

,而如果存放在奇位址開始的地方,就需要

2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該

32bit

數 據。

其實位元組對齊的細節和具體編譯器實現相關,但一般而言,滿足三個準則:1) 

結構體變數的首位址能夠被其最寬基本型別成員的大小所整除;

2) 結構體每個成員相對於結構體首位址的偏移量都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組;例如上面第二個結構體變數的位址空間。

3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末乙個成員之後加上填充位元組。

五 小結與領悟

進行位元組對齊的原因是因為處理器在訪問記憶體的時候並不是乙個位元組乙個位元組操作的, 通常他會一次訪問多個位元組, 比如四個。所以將資料以四位元組對齊方式儲存能夠提高資料訪問效率(其實具體的儲存和對齊方式沒那麼簡單,不說了)。但是, 有時候這種預設的優化並不是我們想要的。比如在設計網路程式的時候,一般情況下我們會定義乙個結構體來表示應用程協議的協議頭,如果通訊雙方的程式是在不同體系結構的計算機編譯出來的(這很有可能),那麼預設的對齊方式是有可能是不同的,這樣解析出來的協議頭必然就是錯的。 另外即使很幸運的對齊方式一樣,在協議頭裡面插入了幾個無關的位元組那也是很不優雅的何況還占用頻寬。

還好,這種對齊方式我們是可以控制的,一般地,可以通過下面的方法來改變預設的對齊方式:

· 使用偽指令#pragma pack (n),編譯器將按照n個位元組對齊;

· 使用偽指令#pragma pack (),取消自定義位元組對齊方式。 

注意:如果#pragma pack (n)中指定的n大於結構體中最大成員的size,則其不起作用,結構體仍然按照size最大的成員進行對界。

詳解4位元組對齊

摘要 編譯器通常採用的預設位元組對齊規則 對於型別t,在n bit系統中,保證變數首位址在min sizeof t n 8 位元組位置上,以保證最少讀週期。以下為原文 其實我也是一條分割線 所謂的位元組對齊,就是各種型別的資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這個就是對齊。我...

詳解4位元組對齊

摘要 編譯器通常採用的預設位元組對齊規則 對於型別t,在n bit系統中,保證變數首位址在min sizeof t n 8 位元組位置上,以保證最少讀週期。以下為原文 其實我也是一條分割線 所謂的位元組對齊,就是各種型別的資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這個就是對齊。我...

詳解4位元組對齊

什麼是4位元組對齊?4位元組對齊有什麼好處,有什麼壞處?分析一波。首先先定義乙個結構體。typedef struct aa aa aa aa 32位處理器,如果沒有採用4位元組對齊,首先結構體變數aa存放在記憶體中的起始位址為0x00,那麼其成員變數a的起始位址為0x00,成員變數b的起始位址為0x...