01
默認字節(jié)對齊
C語言結構體字節(jié)對齊是老生常談的問題了,也是高頻面試題,現在我們來深入研究這個問題,徹底弄懂到底是怎么回事,給你一個結構體定義和平臺機器位數就能手動計算出結構體占用字節(jié)數,現在我們不使用宏#pragma pack,采用默認字節(jié)對齊方式。
先拋出結論:
在一個結構體中第一個成員變量放在偏移為0的位置,以后的變量都存儲在該變量占用字節(jié)數整數倍的地址上。
結構體總大小,必須是內部最大成員變量的整數倍,不足的補齊。
好了,現在我們直接寫個小程序驗證并分析是否真是這樣一回事。
struct st{ short a1; short a2; short a3; }; struct st2{ long a1; short a2; };
這里我們定義了兩個很簡單的結構體,short占用2個字節(jié),struct st我們一眼就知道大小了6個字節(jié),但是struct st2呢?筆者電腦是64位,那么long占用8個字節(jié),short占用2個字節(jié)。我們先來按照結論進行分析,在struct st2中成員變量a1在偏移0處存儲且占用8個字節(jié),成員變量a2占用2個字節(jié),由于8是2的倍數,所以a2在偏移8的位置存儲,又因為有結論2。
我們根據結論2可以得出,struct st2必須占用8的倍數大小,所以struct st2總大小是16個字節(jié),不足的后面補齊?,F在我分別打印出struct st1和struct st2占用字節(jié)數大小和struct st2各個成員變量地址,觀察是否和分析的一樣。
int main() { struct st2 st_val2; printf(“sizeof(long) = %d ”, sizeof(long)); printf(“sizeof(struct st) = %d ”, sizeof(struct st)); printf(“sizeof(struct st2) = %d ”, sizeof(struct st2)); printf(“st_val2 addr = %p ”, &st_val2); printf(“st_val2 a1 addr = %p ”, &st_val2.a1); printf(“st_val2 a2 addr = %p ”, &st_val2.a2); return 0; }
編譯運行輸出:
sizeof(long) = 8 sizeof(struct st) = 6 sizeof(struct st2) = 16 st_val2 addr = 0x7ffee107f3b8 st_val2 a1 addr = 0x7ffee107f3b8 st_val2 a2 addr = 0x7ffee107f3c0
現在我們看一下輸出結果,struct st如我們所愿占用6個字節(jié)大小,struct st2也按照我們分析的一樣占用16個字節(jié)。我們在程序中定義了一個struct st2類型變量st_val2,從輸出中可以看出變量st_val2的a1成員變量和st_val2變量地址一樣,成員變量a2在偏移8處存儲(0x c0 = 0xb8 + 8)。一切如我們所愿,看起來好像挺簡單的,我們知道C語言有豐富的數據類型,下面我們再定義一個更復雜的結構體。
struct st3{ int a1; char a2; short a3; long a4; char a5; };
這個結構體包含了大量數據類型成員變量,再復雜的結構體也能按照我們的結論分析到底占用了幾個字節(jié)。
在struct st3中int型成員變量a1占用4個字節(jié),在偏移0處存儲,char型成員變量a2占用2個字節(jié)那么應該放在2的倍數地址處存儲,a1已經占用了4個字節(jié),所以a2應該在偏移4的地址存儲。
short型成員變量a3占用2個字節(jié),也應該放在2的倍數地址處存儲,所以a3在偏移6的地址處存儲,a2后面填充1個字節(jié)。
long型成員變量a4占用8個字節(jié),應該放在8的倍數地址上存儲,前面我們已經知道a3在偏移6的地址處存儲,且占用2個字節(jié)8 = 6 + 2,所以a4應該在偏移8的地址處存儲。
最后一個char型成員變量a5占用一個字節(jié),那么a5在偏移16地址處存儲。
現在我們計算一下struct st3結構體占用空間大小,從a5偏移出計算16 + 1 = 17。在struct st3中最大成員變量占用8個字節(jié),所以結構體總大小應該是8的倍數,最后結構體總大小是17 + 7 = 24,這里的7個字節(jié)在最后補齊。
我們依舊寫一個小程序輸出struct st3類型變量各個成員變量地址和結構體總大小。
int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }
編譯運行輸出:
sizeof(struct st3) = 24 st_val3 addr = 0x7ffeed0c33b0 st_val3.a1 addr = 0x7ffeed0c33b0 st_val3.a2 addr = 0x7ffeed0c33b4 st_val3.a3 addr = 0x7ffeed0c33b6 st_val3.a4 addr = 0x7ffeed0c33b8 st_val3.a5 addr = 0x7ffeed0c33c0
從輸出我們可以看出,和我們分析的完全一樣。
枚舉類型變量和聯合體類型變量都可以作為結構體的成員變量,在分析這些結構體占用大小時,分析方法和我們上面的一模一樣,只需要把內部任何一種數據類型變量當做一個普通變量看待即可,但是結構體類型成員變量有點不一樣,它不適用于結論2,我們舉個例子。
struct st4{ char a1[3]; int a2; long a3; struct st3 a4; };
在struct st4中我們定義了一個struct st3類型成員變量,前面我們已經分析過了struct st3占用24個字節(jié)。成員變量a1占用3個字節(jié),成員變量a2占用4個字節(jié),所以a2存儲在偏移4的地址上,在a1后面填充一個字節(jié)。成員變量a3占用8個字節(jié),則a3存儲在偏移8的地址上。那么結構體總共占用字節(jié)數大小是:8 + 8 + 24 = 40。
最后我們寫一個程序驗證一下是否如此。
int main() { struct st4 st_val4; printf(“sizeof(struct st4) = %d ”, sizeof(struct st4)); printf(“st4 addr = %p ”, &st_val4); printf(“st_val4.a1 addr = %p ”, &st_val4.a1); printf(“st_val4.a2 addr = %p ”, &st_val4.a2); printf(“st_val4.a3 addr = %p ”, &st_val4.a3); printf(“st_val4.a4 addr = %p ”, &st_val4.a4); return 0; }
編譯運行輸出:
sizeof(struct st4) = 40 st4 addr = 0x7ffeec1263a0 st_val4.a1 addr = 0x7ffeec1263a0 st_val4.a2 addr = 0x7ffeec1263a4 st_val4.a3 addr = 0x7ffeec1263a8 st_val4.a4 addr = 0x7ffeec1263b0
和我們分析的一模一樣。
02
#pragma pack宏的作用
我們看一下下面這段代碼。
#pagma pack(1)int main() { struct st3 st_val3; printf(“sizeof(struct st3) = %d ”, sizeof(struct st3)); printf(“st_val3 addr = %p ”, &st_val3); printf(“st_val3.a1 addr = %p ”, &st_val3.a1); printf(“st_val3.a2 addr = %p ”, &st_val3.a2); printf(“st_val3.a3 addr = %p ”, &st_val3.a3); printf(“st_val3.a4 addr = %p ”, &st_val3.a4); printf(“st_val3.a5 addr = %p ”, &st_val3.a5); return 0; }
這段代碼里我們使用了#pagma pack宏,表示結構體按1字節(jié)對齊。也就是說結構體變量st_val3總大小是內部成員變量占用字節(jié)數總和,沒有字節(jié)填充。
現在編譯運行如下:
sizeof(struct st3) = 16 st_val3 addr = 0x7ffee13a93b8 st_val3.a1 addr = 0x7ffee13a93b8 st_val3.a2 addr = 0x7ffee13a93bc st_val3.a3 addr = 0x7ffee13a93bd st_val3.a4 addr = 0x7ffee13a93bf st_val3.a5 addr = 0x7ffee13a93c7
在struct st3中int型a1占用4字節(jié),char型變量a2占用1個字節(jié),short型變量a3占用2個字節(jié),long型變量a4占用8個字節(jié),char型變量a5占用1個字節(jié),所以總大小是:4 + 1 + 2 + 8 + 1 = 16。如果是#pagma pack(2)呢?相信你可以自己計算了。
編輯:jq
-
C語言
+關注
關注
180文章
7594瀏覽量
135861 -
代碼
+關注
關注
30文章
4722瀏覽量
68234
原文標題:C語言結構體字節(jié)對齊
文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論