數據對齊基本原則請看上一篇博客,這裏針對結構體嵌套以及結構體含有static變量的情況做一些補充:
舉個例子:
一、對於node3,含有靜態數據成員
typedef struct node3 { int a; short b; static int c; }S3;
則sizeof(S3)=8.這裏結構體中包含靜態數據成員,而靜態數據成員的存放位置與結構體實例的存儲地址無關(注意只有在C++中結構體中才能含有靜態數據成員,而C中結構體中是不允許含有靜態數據成員的)。其在內存中存儲方式如下:
|--------int--------| 4字節
|--short-|----|----| 4字節
而變量c是單獨存放在靜態數據區的,因此用siezof計算其大小時沒有將c所佔的空間計算進來。
二、再看結構體嵌套的情況:typedef struct node1 { int a; char b; short c; }S1;
typedef struct node5 { bool a; S1 s1; double b; int c; }S5;sizeof(S5)=32。
對於變量a,其自身對齊參數爲1,#pragma pack(n)爲8,則a的最終對齊參數爲1,爲它分配1字節的空間,它相對於結構體起始地址的偏移量爲0,能被1整除;
對於s1,它的自身對齊參數爲4(對於結構體變量,它的自身對齊參數爲它裏面各個變量最終對齊參數的最大值),#pragma pack(n)爲8,所以s1的最終對齊參數爲4,接下來的地址相對於結構體起始地址的偏移量爲1,不能被4整除,所以需要在a後面填充3字節達到4,爲其分配8字節的空間;(這裏不能把s1看作整體,其大小爲8,所以認爲對齊參數爲8,結構體嵌套的情況要把結構體“打散”來看對齊參數)
對於變量b,它的自身對齊參數爲8,#pragma pack(n)的默認值爲8,則b的最終對齊參數爲8,接下來的地址相對於結構體起始地址的偏移量爲12,不能被8整除,所以需要在s1後面填充4字節達到16,再爲b分配8字節的空間;
對於變量c,它的自身對齊參數爲4,#pragma pack(n)的默認值爲8,則c的最終對齊參數爲4,接下來相對於結構體其實地址的偏移量爲24,能夠被4整除,所以直接爲c分配4字節的空間。
此時結構體所佔字節數爲1+3+8+4+8+4=28字節。
對於整個結構體來說,各個變量的最終對齊參數爲1,4,8,4,最大值爲8,#pragma pack(n)默認值爲8,所以最終結構體的大小必須是8的倍數,因此需要在最後面填充4字節達到32字節。其存儲如下:
|--------bool--------| 4字節
|---------s1---------| 8字節
|--------------------| 4字節
|--------double------| 8字節
|----int----|---------| 8字節
另外可以顯示地在程序中使用#pragma pack(n)來設置系統默認的對齊參數,在顯示設置之後,則以設置的值作爲標準,其它的和上面所講的類似,就不再贅述了,讀者可以自行上機試驗一下。如果需要取消設置,可以用#pragma pack()來取消。
再看一個例子:
struct A
{
short a1;
short a2;
short
a3;
}A1;
struct B
{
char* a1;
char
buf[5];
}B1;
struct C
{
B
a;
A
a2;
}C1;
sizeof(C)=20
前面說的結構體嵌套要“打散”,並不是說把C變成這樣
struct C
{
char* a1;
char buf[5];
short a1;
short a2;
short a3;
}C1;
然後sizeof(C)=16=4+5+1(填充)+2+2+2。
“打散”是爲了找最大對齊字節數。
首先a的起始地址爲0,a作爲結構體,爲其分配的大小爲12,這時候把a2“打散”,求最大對齊字節數爲sizeof(short)=2,12能整除2,所以a2的起始地址爲12,再給a2分配6個字節內存,這樣總共有12+6=18個字節,對於整個結構體C來說,各個變量的最終對齊參數爲4,1,2,2,2,最大值爲4,所以最終結構體的大小必須是4的倍數,因此需要在最後面填充2字節達到20字節。
其存儲如下:
|------char*--------|
4字節
|------char[5]------|
5字節
|-------------------|
3字節
|--------short------|
2字節
|--------short------|
2字節
|--------short------|
2字節
|----------|
2字節