在使用sizeof運算符求算某結構體所佔空間時,並不是簡單的將結構體中各個元素所佔的空間相加,這裏涉及到內存字節對齊問題。從理論上講,對於任何變量的訪問都可以從任何地址開始訪問,但事實並不是如此,實際上訪問特定的變量 只能從特定的地址開始訪問,這就需要各個變量在空間上按照某個特定的規則排列,而不是簡單的順序排列,這就是內存對齊。
內存對齊的原因:
1)某些平臺只能在特定的地址訪問特定的數據;
2)提高存取數據的速度。比如有的平臺每次都是從偶地址處讀取數據,對於一個int型的變量,若從偶地址單元處存放,則只需一個讀取週期即可讀取該變量;但是若從奇地址單元處存放,則需要2個讀取週期讀取該變量。
win32平臺下的微軟C編譯器對齊策略:
1)結構體變量的首地址能夠被其最寬數據類型成員的大小整除。編譯器在爲結構體變量開闢空間時,首先找到結構體中最寬的數據類型,然後尋找內存地址能被該數據類型大小整除的位置,這個位置作爲結構體變量的首地址。而將最寬數據類型的大小作爲對齊標準。
2)結構體每個成員相對結構體首地址的偏移量(offset)都是每個成員本身大小的整數倍,如有需要會在成員之間填充字節。編譯器在爲結構體成員開闢空 間時,首先檢查預開闢空間的地址相對於結構體首地址的偏移量是否爲該成員大小的整數倍,若是,則存放該成員;若不是,則填充若干字節,以達到整數倍的要 求。
3)結構體變量所佔空間的大小必定是最寬數據類型大小的整數倍。如有需要會在最後一個成員末尾填充若干字節使得所佔空間大小是最寬數據類型大小的整數倍。
下面看一下sizeof在計算結構體大小的時候具體是怎樣計算的
1.test1 空結構體
typedefstructnode
{
}S;
|
則sizeof(S)=1;或sizeof(S)=0;
在C++中佔1字節,而在C中佔0字節。
2.test2
typedefstructnode1
{
inta;
charb;
shortc;
}S1;
|
則sizeof(S1)=8。這是因爲結構體node1中最長的數據類型是int,佔4個字節,因此以4字節對齊,則該結構體在內存中存放方式爲
|--------int--------| 4字節
|char|----|--short-| 4字節
總共佔8字節
3.test3
typedefstructnode2
{
chara;
intb;
shortc;
}S2;
|
則siezof(S3)=12.最長數據類型爲int,佔4個字節。因此以4字節對齊,其在內存空間存放方式如下:
|char|----|----|----| 4字節
|--------int--------| 4字節
|--short--|----|----| 4字節
總共佔12個字節
4.test4 含有靜態數據成員
typedefstructnode3
{
inta;
shortb;
staticintc;
}S3;
|
則sizeof(S3)=8.這裏結構體中包含靜態數據成員,而靜態數據成員的存放位置與結構體實例的存儲地址無關(注意只有在C++中結構體中才能含有靜態數據成員,而C中結構體中是不允許含有靜態數據成員的)。其在內存中存儲方式如下:
|--------int--------| 4字節
|--short-|----|----| 4字節
而變量c是單獨存放在靜態數據區的,因此用siezof計算其大小時沒有將c所佔的空間計算進來。
5.test5 結構體中含有結構體
typedefstructnode4
{
boola;
S1 s1;
shortb;
}S4;
|
則sizeof(S4)=16。是因爲s1佔8字節,而s1中最長數據類型爲int,佔4個字節,bool類型1個字節,short佔2字節,因此以4字節對齊,則存儲方式爲
|-------bool--------| 4字節
|-------s1----------| 8字節
|-------short-------| 4字節
6.test6
typedefstructnode5
{
boola;
S1 s1;
doubleb;
intc;
}S5;
|
則sizeof(S5)=32。是因爲s1佔8字節,而s1中最長數據類型爲int,佔4字節,而double佔8字節,因此以8字節對齊,則存放方式爲:
|--------bool--------| 8字節
|---------s1---------| 8字節
|--------double------| 8字節
|----int----|---------| 8字節
7.test7
若在程序中使用了#pragma pack(n)命令強制以n字節對齊時,默認情況下n爲8.
則比較n和結構體中最長數據類型所佔的字節大小,取兩者中小的一個作爲對齊標準。
若需取消強制對齊方式,則可用命令#pragma pack()
如果在程序開頭使用命令#pragma pack(4),對於下面的結構體
typedefstructnode5
{
boola;
S1 s1;
doubleb;
intc;
}S5;
|
則sizeof(S5)=24.因爲強制以4字節對齊,而S5中最長數據類型爲double,佔8字節,因此以4字節對齊。在內存中存放方式爲:
|-----------a--------| 4字節
|--------s1----------| 4字節
|--------s1----------| 4字節
|--------b-----------| 4字節
|--------b-----------| 4字節
|---------c----------| 4字節
總結一下,在計算sizeof時主要注意一下幾點:
1)若爲空結構體,則只佔1個字節的單元
2)若結構體中所有數據類型都相同,則其所佔空間爲 成員數據類型長度×成員個數
若結構體中數據類型不同,則取最長數據類型成員所佔的空間爲對齊標準,數據成員包含另一個結構體變量t的話,則取t中最 長數據類型與其他數據成員比較,取最長的作爲對齊標準,但是t存放時看做一個單位存放,只需看其他成員即可。
3)若使用了#pragma pack(n)命令強制對齊標準,則取n和結構體中最長數據類型佔的字節數兩者之中的小者作爲對齊標準。
另外除了結構體中存在對齊之外,普通的變量存儲也存在字節對齊的情況,即自身對齊。編譯器規定:普通變量的存儲首地址必須能被該變量的數據類型寬度整除。
測試程序:
1 |
typedefstructnode |
2 |
{ |
3 |
}S; |
1 |
typedefstructnode5 |
2 |
{ |
3 |
boola; |
4 |
S1
s1; |
5 |
doubleb; |
6 |
intc; |
7 |
}S5; |