1. 程式人生 > >#pragma pack(n)記憶體對齊方式

#pragma pack(n)記憶體對齊方式

在C語言中,結構是一種複合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些複合資料型別(如陣列、結構、聯合等)的資料單元。在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲存,第一個成員的地址和整個結構的地址相同。

例如,下面的結構各成員空間分配情況:
struct test
{
     char x1;
     short x2;
     float x3;
     char x4;
};

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

更改C編譯器的預設位元組對齊方式
在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
     · 使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊。
     · 使用偽指令#pragma pack (),取消自定義位元組對齊方式。

另外,還有如下的一種方式:
     · __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。

如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。
     · __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。

以上的n = 1, 2, 4, 8, 16... 第一種方式較為常見。

最後得到了以下結論:
    1. 成員的對齊是按宣告順序進行的;
    2. 對齊值由編譯指示和最大成員兩者較小的值決定;
    3. 未對齊到對齊值的成員一起形成塊對齊(聯合對齊);
    4. 上一個(下一個)對齊採用自己較大則不變,自己較小則填充自己對齊到上一個(下一個)大小;
    5. 每成員對齊:如果前面已對齊到對齊值,下一個對齊自己。如果前面未對齊到對齊值,如果加上下一個成員不大於對齊值,下一個對齊自己,否則填充自己塊對齊到對齊值。
    6. 最後還未對齊到對齊值的,填充空間塊對齊到對齊值。

從這些結論,可以得到:
    1. 以上的對齊原則其實是儘量整齊排列、儘量節省記憶體。
    2. 宣告成員應該儘量避免不同型別錯雜開來,最好採用從小到大或者從大到小的順序(錯開後,會因為上對齊和下對齊而增加填充開銷)。
    3. 編譯器預設採用8位元組對齊主要是因為最大基本型別為8自己(以前自己不明白,在論壇提過問,後來,以為是SSE指令的原因)。
    4. 手算sizeof是沒有必要的,負責的(可以先對齊出對齊塊,用塊數乘對齊值)。