關於結構體的字節對齊是什麼,就不贅述,再此附上一篇文章,介紹字節對齊:http://www.linuxsong.org/2010/09/c-byte-alignment/
這裡的結構體字節對齊的數據類型都是基本數據類型,如果結構體的定義中含有結構體成員呢?
網上有很多人寫博客談到這個問題,都認為該結構體成員應該被看做一個整體,按照整體的字節數來進行字節對齊,選擇首地址。但是經過測試,這種說法是不對的。
復制代碼
1 struct s1{
2 char c1;
3 char c2;
4 char c3;
5 char c4;
6 };
7
8 struct s2{
9 char a;
10 struct s1 s;
11 };
復制代碼
對於上述代碼,顯然sizeof(struct s1) = 4。如果將struct s1看做整體,來進行字節對齊的話,sizeof(struct s2)應該是等於8。但是該段代碼在gcc和VS2010下測試的結果都是5。
所以,s2的內存布局如下
s2 : char | char char char char |
s的首地址並非為4,而是char類型長度的整數倍
同樣,對於結構體:
復制代碼
1 struct S1
2 {
3 char a;
4 short b;
5 };
6
7 struct S2
8 {
9 char a;
10 struct S1 s;
11 };
復制代碼
上述代碼中,sizeof(struct S1) = 4,而sizeof(struct S2) = 6。
S2的內存布局是:
S2 : char **** | char **** short short
s的首地址並非為4,而是short類型長度的整數倍,結構體S2的長度並非結構體S1長度的整數倍,而是short類型長度的整數倍
相對的,我們可以看一下結構體:
struct S
{
char a;
char b;
short c;
};
sizeof(S) = 4。
其內存布局為:
S : char char short short
對比S2和S,我們看到,S2中的s結構體成員確實是仍按它作為S1結構體的整體(占用4個字節)來布局的。
那麼我們可以得出這情形下的字節對齊問題的結論:
1)結構體中的結構體成員,是按照原結構體的內存布局在新結構體中進行布局的;
2)結構體中的結構體成員,其內存首地址為該成員中最長數據類型的整數倍;
3)結構體的整體長度與其中的結構體成員無關,而是整個結構體(包括結構體成員內部)中的最長數據類型的整數倍。
得出結論後,我們回到處理器本身,來探討一下編譯器之所以這麼處理的原因。
從CPU結構角度看內存對齊有一篇比較易懂的文章:
http://hi.baidu.com/maikeai/item/4976f24cc0f905d3c1a592a0
從該文章中,我們可以知道,之所以需要內存對齊,是CPU為了讀取數據時減少讀取存儲器的次數,提高效率。
對於32位的CPU,一次最多讀入雙字的32位數據,而對於超過32位的數據,在32位的計算機上,就必須多次讀取存儲器,這與是否字節對齊無關。
我們可以看到,字節對齊,其實實質上是在拿存儲器的空間,換CPU的時間。
所以,對於C語言內置的數據類型,可以很好地通過內存對齊來節省時間,同時其浪費的存儲器空間也是可控的。
但是,對於結構體成員來說,因為它的長度是不可控的,所以如果強行按整體內存對齊,首先可能因為長度過長,即使內存對齊需要的存儲器讀取次數也很多,導致優化的時間不明顯,另外,也可能導致浪費的存儲器空間太大。所以,最簡單的平衡空間和時間的方法,就是按照該結構體成員中最長數據類型來進行字節對齊,這樣最有可能減少存儲器讀取次數,也使浪費的存儲器空間可控。