結構體是一種復合數據類型,通常編譯器會自動的進行其成員變量的對齊,已提高數據存取的效率。在默認情況下,編譯器為結構體的成員按照自然對齊(natural alignment)條方式分配存儲空間,各個成員按照其聲明順序在存儲器中順序存儲。自然對齊是指按照結構體中成員size最大的對齊,在cl編譯器下可以使用
#pragma pack(n)
來指定結構體的對齊方式。
默認對齊方式
在默認對齊方式下,結構體成員的內存分配滿足下面三個條件
- 結構體第一個成員的地址和結構體的首地址相同
- 結構體每個成員地址相對於結構體首地址的偏移量(offset)是該成員大小的整數倍,如果不是則編譯器會在成員之間添加填充字節(internal adding)。
- 結構體總的大小要是其成員中最大size的整數倍,如果不是編譯器會在其末尾添加填充字節(trailing padding)。
下面是一個示例:
struct s1{
char ch;
int a;
double b;
char c1;
};
struct s2{
char ch;
int a;
double b;
};
int main()
{
cout << "s1的大小: " << sizeof(struct s1) << endl;
cout << "ch的地址偏移是 " << offsetof(s1, ch) << endl;
cout << "a 的地址偏移是 " << offsetof(s1, a) << endl;
cout << "b 的地址偏移是 " << offsetof(s1, b) << endl;
cout << "c1的地址偏移是 " << offsetof(s1, c1) << endl;
cout << "=====================================" << endl;
cout << "s2的大小: " << sizeof(struct s2) << endl;
cout << "ch的地址偏移是 " << offsetof(s2, ch) << endl;
cout << "a 的地址偏移是 " << offsetof(s2, a) << endl;
cout << "b 的地址偏移是 " << offsetof(s2, b) << endl;
getchar();
return 0;
}
代碼中 offsetof函數可以得到結構體成員相對於該結構體首地址的偏移量。
其運行結果如下圖
對於結構體s1來說,
- ch是其第一個成員故其地址和結構體的地址是相同的也就是說偏移量為0;
- a是int型其大小為4個字節,按照條件(2) 結構體每個成員地址相對於結構首地址的偏移量(offset)是該成員大小的整數倍,如果不是則編譯器會在成員之間添加填充字節,所以其地址偏移應該是4,也就說編譯器在第一個成員ch后面填充了3個字節。
- b是double型占8個字節,其地址偏移應該是8的整數倍,由於a的地址偏移是4其大小為4個字節,正好b的偏移地址是8,不需要填充字節。
- c1是char型占1個字節,偏移地址是16(b的偏移地址是8大小也是8,中間也沒有填充字節)。
- 這時成員ch占1個字節后面有3個字節的填充,a占4個字節后面無填充,b占8個字節后面無填充,c1占1個字節,s1總的大小是\(1 + 3 + 4 + 8 + 1 = 17\)。按照條件(3)結構體總的大小需是其最大成員所占空間的整數倍,其最大的成員b占有8字節,17顯然是不符合條件的,所以需要在結構體的末尾填充7個字節,最后結構體總的大小是24字節。
結構體s2和s1的成員是非常相似的,唯一的區別是其末尾沒有最后7個字節的填充,所以其大小是16個字節,這里用於和s1做對比說明s1末尾的填充字節。
指定對齊方式
可以使用#pragma pack(N)來指定結構體成員的對齊方式
對於指定的對齊方式,其成員的地址偏移以及結構的總的大小也有下面三個約束條件
- 結構體第一個成員的地址和結構體的首地址相同
- 結構體每個成員的地址偏移需要滿足:N大於等於該成員的大小,那么該成員的地址偏移需滿足默認對齊方式(地址偏移是其成員大小的整數倍);N小於該成員的大小,那么該成員的地址偏移是N的整數倍。
- 結構體總的大小需要時N的整數倍,如果不是需要在結構體的末尾進行填充。
- 如果N大於結構體成員中最大成員的大小,則N不起作用,仍然按照默認方式對齊。
示例仍然是上面的s1和s2,不過使用
#pragma pack(4)
設定按照4字節對齊
運行結果
結果分析
- ch是其第一個成員故其地址和結構體的地址是相同的也就是說偏移量為0;
- a占4個字節,和設定的對齊方式相等,所以其地址偏移是其大小的整數倍為4。
- b占8個字節,大於設定的對齊方式4,所以其地址偏移是N的整數倍為8。
- c1占1個字節,小於設定的對齊方式,所以其地址偏移是其大小的整數倍為16。
- 總的大小17個字節,不是N(4)的整數倍,所以在結構體的末尾填充3個字節,總的大小為20個字節。
說明:
- 在使用#pragma pack設定對齊方式一定要是2的整數冪,也就是(1,2,4,8,16,...),不然不起作用的,仍然按照默認方式對齊。
- 當結構體中有其他的結構體作為成員時,計算最大成員是不能把結構體成員作為一個整體來計算,要看其每個成員的大小。