因爲我看C++對象模型的時候,遇到了幾個內存佈局都是有關於對齊的一些細節,故此對結構體對齊再做一份小結,有人說:結構體對齊這個東西是依賴於編譯器的,因此不用去研究,真的嘛?
也許是,也許不是,要看你是做那個行業的了,如果你是做系統地層,網絡通訊,嵌入式系統的,一個字節的節省,也許對你是很大的期望呢。雖然具體的對齊方式是因編譯器而異,但是對齊的基本原理是不變的,那個原理也許能指導我們編寫程序的時候按照某個原則去進行。
不過,既然你用到了C或者C++,就多數是和系統底層有緣之人了,你說是嘛?呵呵。
現在先說在windows x86 32位機子下的MS vs2005編譯器(就是:cl.exe for x86)下的對齊規則:
比如以下的結構體定義:
struct A
{
int i;
double d;
char c;
};
問sizeof(struct A)在vs2005的大小?
現在說一下cl.exe(就是微軟vs2005的編譯器進程)在默認情況下是怎麼做的:
1、對齊量的確定:找到A中最大的基本類型成員的大小,在本例中是8(double的大小).
2、當定義一個結構體變量struct A aA; 的時候,aA的起始地址要被由1確定的對齊量整除,在這個例子裏,aA的起始地址一定要能被8整除)
2、然後開始分配int i,4字節的空間;再分配double d;注意double 是8字節,所以要分配在被8整除的地方,因此int i後面空了4字節填充;
3、然後,分配char c;1字節,這個時候struct A的大小是4(這是int i;的) + 4 (這是填充的) + 8(這是double d;的) + 1 (這是char c;的)= 17;
4、最後,要求結構體總的大小要能被由1確定的對齊量整除,在這裏是說struct A的大小要能被8整除,所以還要加上7字節的填充字符,一共是24字節。
從這裏可以看出,vs2005的編譯器的結構體填充有這樣的規則(默認情況下,這個默認情況可以通過工程屬性上面的選項修改):
1、對齊量的確定:結構體中最大的數據成員的字節數
2、當定義一個結構體變量的時候,起始地址一定能被確定的對齊量整除;
2、分配每一個成員的時候,該成員相對於起始地址的偏移(offset)要能被該成員的大小整除;
3、結構體總的大小能被確定的對齊量整除;
擴充的字節叫做pad(填充字節)
下面看linux g++ 3.4.3編譯器在x86 的32位機子下的默認規則,那相對簡單一些:(相當於VS2005中將對齊情況改成4字節對齊)
對齊的方式默認是4字節,所以每個成員都按照4字節方式對齊即可。上面的結構體的大小就是:
4 (int i;的大小) + 8(double 的大小) + 1(char 的大小) + 3 (爲了4字節填充而補充的字節) = 16字節;
至於gcc的結構體變量的首地址分配的特徵一時也找不到,還望各位多多指教。謝謝。
最後,如果定義一個沒有任何成員的結構體,struct A{}; 該大小是多少呢?是1字節。如果定義了兩個結構體變量struct A a1, a2; 1字節的填充能夠使a1和a2的地址區分開來!
還有其他的編譯器和操作系統就不是我所能知的了。
那麼,這些規則也許不盡相同,但是給了我們一個編程時候的注意點,就是:
定義結構體的時候,成員最好能從大到小來定義,那樣能相對的省空間。(至少不會比別的順序差,一般情況下哈。)
比如以上的結構體,如果能這樣定義:
struct A
{
double d;
int i;
char c;
};
那麼,無論是windows下的vs2005,還是linux下的gcc,都是16字節。
對齊情況還可以通過各個編譯器給出的特性在代碼中改,不過我沒用過,就沒有發言權,各位又需要可以參考別的文章。
以上的過程和推論是我看別的文章和上機試驗的結果,如有不對之處,請各位指教,謝謝。
再說一點,對於有嵌套的情況,vs2005下的對齊我也不大確定,不過我的分析如下:
struct A
{
char c;
double d;
int i;
};
struct B
{
struct A a;
char c;
};
這種情況下,分析B的大小;
1、對齊量的確定:包括A中的各成員比較的,遞歸下去,比較各個基本類型成員的大小,去最大值,爲8(double的大小);
2、起始地址和上面的一樣,要求B的變量的其實地址能被8整除;
3、關於B中struct A a;的偏移和大小和將a單獨作爲結構體變量的時候一樣(那樣方便賦值運算)
剩下的和上面的規則相同;
所以:B的大小就是24(struct A)+ 1(char) +7(pad) = 32字節;
不知正確與否?還請諸位評判。