例一:
#include <iostream>
using namespace std;
struct X
{
char a;
int b;
double c;
}S1;
void main()
{
cout << sizeof(S1) << endl;
cout << sizeof(S1.a) << endl;
cout << sizeof(S1.b) << endl;
cout << sizeof(S1.c) << endl;
}
比如例一中的結構體變量S1定義之后,經測試,會發現sizeof(S1)= 16,其值不等於sizeof(S1.a) = 1、sizeof(S1.b) = 4和 sizeof(S1.c) = 8三者之和,這里面就存在存儲對齊問題。
原則一:結構體中元素是按照定義順序一個一個放到內存中去的,但並不是緊密排列的。從結構體存儲的首地址開始,每一個元素放置到內存中時,它都會認為內存是以它自己的大小來划分的,因此元素放置的位置一定會在自己寬度的整數倍上開始(以結構體變量首地址為0計算)。
比如此例,首先系統會將字符型變量a存入第0個字節(相對地址,指內存開辟的首地址);然后在存放整形變量b時,會以4個字節為單位進行存儲,由於第一個四字節模塊已有數據,因此它會存入第二個四字節模塊,也就是存入到4~8字節;同理,存放雙精度實型變量c時,由於其寬度為8,其存放時會以8個字節為單位存儲,也就是會找到第一個空的且是8的整數倍的位置開始存儲,此例中,此例中,由於頭一個8字節模塊已被占用,所以將c存入第二個8字節模塊。整體存儲示意圖如圖1所示。
考慮另外一個實例。
例二:
struct X
{
char a;
double b;
int c;
}S2;
在例二中僅僅是將double型的變量和int型的變量互換了位置。測試程序不變,測試結果卻截然不同,sizeof(S2)=24,不同於我們按照原則一計算出的8+8+4=20,這就引出了我們的第二原則。
原則二:在經過第一原則分析后,檢查計算出的存儲單元是否為所有元素中最寬的元素的長度的整數倍,是,則結束;若不是,則補齊為它的整數倍。
例二中,我們分析完后的存儲長度為20字節,不是最寬元素長度8的整數倍,因此將它補齊到8的整數倍,也就是24。這樣就沒問題了。其存儲示意圖如圖2所示。
掌握了這兩個原則,就能夠分析所有數據存儲對齊問題了。再來看幾個例子,應用以上兩個原則來判斷。
例三:
struct X
{
double a;
char b;
int c;
}S3;
首先根據原則一來分析。按照定義的順序,先存儲double型的a,存儲在第0~7個字節;其次是char型的b,存儲在第8個字節;接下來是int型的c,順序檢查后發現前面三個四字節模塊都被占用,因此存儲在第4個四字節模塊,也就是第12~15字節。按照第一原則分析得到16個字節,16正好是最寬元素a的寬度8的整數倍,因此結構體變量S3所占存儲空間就是16個字節。存儲結構如圖3所示。
例四:
struct X
{
double a;
char b;
int c;
char d;
}S4;
仍然首先按照第一原則分析,得到的字節數為8+4+4+1=17;再按照第二原則補齊,則結構體變量S4所占存儲空間為24。存儲結構如圖4所示:
例五:
struct X
{
double a;
char b;
int c;
char d;
int e;
}S5;
同樣結合原則一和原則二分析,可知在S4的基礎上在結構體內部變量定義最后加入一個int型變量后,結構體所占空間並未增加,仍為24。存儲結構示意圖如圖5所示。
例六:
如果將例五中加入的變量e放到第一個定義的位置,則情況就不同了。結構體所占存儲空間會變為32。其存儲結構示意圖如圖6所示。
struct X
{
int e;
double a;
char b;
int c;
char d;
}S6;
補充:前面所介紹的都是元素為基本數據類型的結構體,那么含有指針、數組或是其它結構體變量或聯合體變量時該如何呢?
1.包含指針類型的情況。只要記住指針本身所占的存儲空間是4個字節就行了,而不必看它是指向什么類型的指針。
例七:
struct X struct Y struct Z
{ { {
char *a; int *b; double *c;
}; }; };
經測試,可知sizeof(X)、sizeof(Y)和sizeof(Z)的值都為4。
2.含有構造數據類型(數組、結構體和聯合體)的情況。首先要明確的是計算存儲空間時要把構造體看作一個整體來為其開辟存儲空間;其次要明確的是在最后補齊時是按照所有元素中的基本數據類型元素的最長寬度來補齊的,也就是說雖然要把構造體看作整體,但在補齊的時候並不會按照所含結構體所占存儲空間的長度來補齊的(即使它可能是最長的)。
例八:
struct X
{
char a;
int b;
double c;
};
struct Y
{
char a;
X b;
};
經測試,可知sizeof(X)為16,sizeof(Y)為24。即計算Y的存儲長度時,在存放第二個元素b時的初始位置是在double型的長度8的整數倍處,而非16的整數倍處,即系統為b所分配的存儲空間是第8~23個字節。
如果將Y的兩個元素char型的a和X型的b調換定義順序,則系統為b分配的存儲位置是第0~15個字節,為a分配的是第16個字節,加起來一共17個字節,不是最長基本類型double所占寬度8的整數倍,因此要補齊到8的整數倍,即24。測試后可得sizeof(Y)的值為24。
由於結構體所占空間與其內部元素的類型有關,而且與不同類型元素的排列有關,因此在定義結構體時,在元素類型及數量確定之后,我們還應該注意一下其內部元素的定義順序。