字節對齊原因:
1)平台原因(移植原因):不是所有的硬件平台都能訪問任意地址上的任意數據,某些硬件平台只能在某些地址處取某些特定類型的數據,否則拋出硬件異常
2)硬件原因:經過內存對齊之后,CPU的內存訪問速度大大提升,幫助cpu尋址。
【注意】(對齊位數跟處理器位數和編譯器都有關)VS, VC等編譯器默認是#pragma pack(8),所以測試我們的規則會正常;注意gcc默認是#pragma pack(4),並且gcc只支持1,2,4對齊。
改變缺省的對界條件(指定對界):
- 使用偽指令#pragma pack (n),C編譯器將按照n個字節對齊。
- 使用偽指令#pragma pack(),取消自定義字節對齊方式。
對齊規則
規則一:結構體的每個成員相對於結構體的首地址的偏移量,都是基本成員大小的整數倍。比如
struct{ int a; //4 首地址&a char b; //1 第二個成員&b相對於第一個成員&a是整數倍 double c; //8 }
規則二: 如果有嵌套結構體或者有聯合類型,那么內嵌結構體的第一個成員變量在外結構體中的偏移量,是內嵌結構體或聯合體中那個數據類型大小最大的成員變量的倍數。
規則三: 整個結構體的大小要是這個結構體內數據類型大小最大的成員變量的倍數。如果有內嵌結構體,那么取內嵌結構體中數據類型大小最大的成員變量作為計算外結構體整體大小的依據。
最終有效的字節對齊值:
在自身字節對齊值和編譯器要求的字節對齊值中取較小的。
比如:Linux 64位默認對齊為8位,下面輸出為16,long在64位下為8位
typedef struct Test { short a; long e; } t1; int main() { printf("%d\n", sizeof(t1)); return 0; }
但是在前面加了 #pragma pack(4)
#pragma pack(4) typedef struct Test { short a; long e; } t1;
輸出將變為12
結構體成員定義順序不同,結構體大小也不同。
實例說明
st1占8字節,st2占12字節
str3占16字節,str4占12字節
st5占用16字節,st6占用24字節
嵌套的結構體例子
typedef struct TEST{ int na; char cb; char cc; int nd; char cf; struct TT{ int ng; long long llh; }tt; char ci; }test;
內存分布圖
如上圖所示,根據規則一,nd在結構體內的位置必須滿足是其自身數據類型大小的整數倍,且倍數要取滿足條件的最小倍數。因為nd前三個成員變量總大小是6Bytes,nd作為int類型,其數據類型大小是4Bytes,滿足條件的最小倍數是2,所以nd在結構體中的偏移量是8,故填充2Bytes。
根據規則二,ng作為內嵌結構體的第一個成員變量,它在外結構體中的偏移量要滿足——其內嵌結構體中最大數據類型大小的倍數。在計算cf偏移量后,當前偏移量是13,而內嵌結構體中最大數據類型為long long,其大小為8,ng的偏移量必須是8的倍數,且取大於13的最小倍數,即2。所以ng前要填充3Bytes。
根據規則三,計算完ci后,當前偏移量是33。如果要滿足test結構體的大小是其中最大數據成員類型大小的倍數,在此例中是內嵌結構體的成員變量llh,即8的倍數。所以要填充7Bytes。
空結構體
空結構體在C下為0,在C++下為1
在C++下,空類和空結構體的大小是1(編譯器相關),這是為什么呢?為什么不是0?
這是因為,C++標准中規定,“no object shall have the same address in memory as any other variable” ,就是任何不同的對象不能擁有相同的內存地址。 如果空類大小為0,若我們聲明一個這個類的對象數組,那么數組中的每個對象都擁有了相同的地址,這顯然是違背標准的。
C++標准規定不同的對象不能擁有相同的地址。那么怎樣才能保證這個條件被滿足呢?最簡單的方法莫過於不允許任何類型的大小為0。所以編譯器為每個空類或者空結構體都增加了一個虛設的字節(有的編譯器可能加的更多),這樣這些空類和空結構的大小就不會是0,就可以保證他們的對象擁有彼此獨立的地址。
字節對齊的一些陷阱
如果在結構體中嵌套了結構體,要先把中間的結構體算出來
1.如果嵌套的結構體中沒有變量名,就表示整個嵌套的結構體只是一個類型,它就不會占用空間
#pragma pack(4) typedef struct Test { short a; struct t //如果沒有t,成員的變量空間都要計算 { int b; double c; char d; }; //沒有變量名,不占用空間 long e; }; //輸出結果:12
2.如果結構體中沒有變量名,也沒有結構體名,那么結構體中的所有成員空間都要計算在內,並且結構體中的成員都要對齊
#pragma pack(4) typedef struct Test { short a; struct //如果沒有結構體名 { int b; double c; char d; };//有變量名或沒有變量名 long e; }t1; //輸出結果:28
結構體定義的建議
相同類型的變量最好放在一起,會減小結構體的空間
變量類型從小到大的順序比較合適