之前若是有人拿個結構體或者聯合體問我這個結構占用了多少字節的內存,我一定覺得這個人有點low, 直到某某公司的一個實習招聘模擬題的出現,讓我不得不重新審視這個問題,
該問題大致如下:
typedef struct _A{ char a; int b; float c; double d; int *pa; char* pc; short e; }A; #pragma pack(pop) int main(int argc, char *argv[]) { printf("size = %d\n",sizeof(A)); return 0; }
程序輸出結果是()。
A size= 48
B size= 44
C size= 40
D size= 36
乍一看,這還不簡單嗎, 1+4+4+8+4+4+2=27個字節,這個最少的答案也是36啊!
有同學提示說數據對齊,這才注意到預編譯指令#pragma pack(pop), 一百度,得到這樣的答案:
作用:指定結構體、聯合以及類成員的packing alignment;
語法:#pragma pack( [show] | [push | pop] [, identifier], n )
說明:
1,pack提供數據聲明級別的控制,對定義不起作用;
2,調用pack時不指定參數,n將被設成默認值;
3,一 旦改變數據類型的alignment,直接效果就是占用memory的減少,但是performance會下降;
語法具體分析:
1,show:可選參數;顯示當前packing aligment的字節數,以warning message的形式被顯示;
2,push:可選參數;將當前指定的packing alignment數值進行壓棧操作,這裡的棧是the internal compiler stack,同時設置當前的packing alignment為n;如果n沒有指定,則將當前的packing alignment數值壓棧;
3,pop:可選參數;從internal compiler stack中刪除最頂端的record;如果沒有指定n,則當前棧頂record即為新的packing alignment數值;如果指定了n,則n將成為新的packing aligment數值;如果指定了identifier,則internal compiler stack中的record都將被pop直到identifier被找到,然後pop出identitier,同時設置packing alignment數值為當前棧頂的record;如果指定的identifier並不存在於internal compiler stack,則pop操作被忽略;
4,identifier:可選參數;當同push一起使用時,賦予當前被壓入棧中的record一個名稱;當同pop一起使用時,從internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier沒有被找到,則忽略pop操作;5,n:可選參數;指定packing的數值,以字節為單位;缺省數值是8,合法的數值分別是1、2、4、8、16。
因為沒有push操作,internal compiler stack為空,所以這個條預編譯指定在這裡其實是沒有任何作用的,使用下面這條指令查看當前的packing alignment = 8, 為默認的對齊參數。
#pragma pack(show)
再來看一下數據對齊的規則,復雜數據對齊主要分為數據成員的對齊和復雜數據整體對齊,兩者都可以總結為
“在自身對齊參數和指定對齊參數中,選擇小的對齊”
數據成員自身對齊參數為類型占用字節的長度,而復雜數據的自身對齊參數為最大的數據成員自身對齊參數,所謂對齊N,就是使得偏移量offset滿足 offset%N = 0,並且使得數據長度盡可能短,第一數據成員的偏移量為0,
因此我們可以分析
成員變量的對齊
a的偏移量為 0,占用一個字節
此時的偏移量為0+1 =1,b的自身對齊參數即其長度為4,packing alignment =8, 顧應對齊4,顧其偏移量為4,(1+3)%4=0
此時偏移量為4+4= 8 ,而c也應對齊4, 8%4 = 0 ,故其偏移量為8
此時偏移量為8+4 = 12,而d的自身長度為8, 指定對齊參數也為8,故其對齊參數為8,所以其偏移量應為12+4=16,因為16%8 = 0
此時偏移量為16+8 = 24,而pa的對齊參數為4,24%4= 0,故其偏移量為24
此時偏移量為24+4 = 28,同理pc的偏移量也為28
此時偏移量為24+4 = 32,e的對齊參數為2,32%2 =0,故其偏移量為32,長度為2,數據成員對齊後結構體A的大小為
32+2 = 34個字節
整體對齊
結構體A中,最大的成員變量的長度為8,指定的packing alignment也是8,故其應對齊8,而數據成員對齊後其大小為34, 整體對齊後其大小應為40, 因為40%8 = 0。