結構體記憶體對齊(C語言)
結構體記憶體對齊規則:
1.第一個成員在與結構體變數偏移量為0 的地址處。
2.其他成員變數要對齊到某個數字(對其數)的整數倍的地址處。
對齊數 = 編譯器預設的一個對齊數 與 該成員大小的 較小值。
vs 中預設的值為 8
3.結構體總大小為最大對齊數(每個成員變數都有一個對齊數)的整數倍。
4.如果嵌套了結構體的情況,巢狀的結構體對齊到自己的最大對齊數的整數倍處,結構體的整體大小就是所有最大對齊數(包含巢狀結構體的對齊數)的整數倍。
實際應用
#include<stdio.h> struct s1 { char c1; int a; char c2; };struct s2 { char c1; char c2; int a; }; int main() { struct s1 s1 = {0}; printf("%d\n",sizeof(s1)); struct s2 s2 = {0}; printf("%d\n",sizeof(s2)); return 0; }
這段程式碼輸出的結果為:
12
8
下面我們來逐一分析:
s1
c1為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。
a為int型,佔四個位元組,對齊數是4,對齊到4的整數倍的地址處,即偏移量為4開始的地址空間。
c2為char型,佔一個位元組,對齊數是1,對齊到1的整數倍的地址處,即偏移量為8開始的地址空間。
結構體總大小為最大對齊數的整數倍,所以為對齊數4的整數倍,現在已經用了9個位元組的空間,那麼總大小就是12個位元組空間。
所以輸出結果是12。
s2
c1為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。
c2為char型,佔一個位元組,對齊數是1,對齊到1的整數倍的地址處,即偏移量為1開始的地址空間。
a為int型,佔四個位元組,對齊數是4,對齊到4的整數倍的地址處,即偏移量為4開始的地址空間。
結構體總大小為最大對齊數的整數倍,所以為對齊數4的整數倍,現在已經用了8個位元組的空間,那麼總大小就是8個位元組空間。
所以輸出結果是8。
結構體的巢狀使用情況
#include<stdio.h> struct s3 { double d; char c; int i; }; struct s4 { char a; struct s3 s3; double b; }; int main() { struct s4 s4; printf("%d\n",sizeof(s4)); return 0; }
結果為:32
由以上方法容易得出struct S3佔16個位元組,最大對齊數為:8。
a為char型,佔一個位元組,第一個成員即 c1 在與結構體變數偏移量為0 的地址處。
第二個成員是結構體巢狀使用,結構體S3變數s3,剛才已經得出佔16個位元組,最大對齊數是8,所以對齊到偏移量為8的地址空間。
b為double型,佔八個位元組,對齊數是8,對齊到8的整數倍的地址處,即偏移量為24開始的地址空間。
結構體總大小為最大對齊數的整數倍,所以為對齊數8的整數倍,現在已經用了32個位元組的空間,那麼總大小就是32個位元組空間。
所以輸出結果是32。
s3
s4
為什麼存在記憶體對齊:
1.平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料的;某些硬體平臺只能在某些地址處取某些特定型別的資料,否者丟擲硬體異常。
2.效能原因:資料結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要做兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。
總體來說:
結構體的記憶體對齊是拿空間來換取時間的做法。
那在設計結構體的時候,我們既要滿足對齊,又要節省空間,如何做到:
讓佔用空間小的成員儘量集中在一起。
struct s1 { char c1; int a; char c2; }; struct s2 { char c1; char c2; int a; };
s1 和 s2 型別的成員一模一樣,但是s1和s2所佔的空間的大小有了一些區別。
設定預設對齊數:
#pragma pack(8)//設定預設對齊數為8 struct s1 { char c1; int i; char c2; }; #pragma pack()//取消設定的預設對齊數,還原為預設