按最大的union中的情況分配,然後再看對齊補齊。
本來,一般是不自己計算sizeof的,知道記憶體對齊會對sizeof有影響,所以從來不手算,而是代碼裡寫上sizeof。今天又看到http://blog.vckbase.com/smileonce/archive/2005/08/08/10658.html,翻來了http://blog.vckbase.com/billdavid/archive/2004/06/23/509.html ,自己想想還是也記錄一下,萬一以後自己真的也要計算sizeof,忘了,還能有個提示,也給不是很明白的朋友一個參考。
struct sample1
{
char a; /// sizeof(char) = 1
double b; /// sizeof(double) = 8
};
///default( 預設#pragam pack(8) ——VC6和VC71,其它編譯器,個人未知 )
///1+8 = 9 —> 16( 8 < 9 < 16 )
#pragma pack( 4 )
///1+8 = 9 —> 12( 8 < 9 < 12 )
#pragma pack( 2 )
///1+8 = 9 —> 10( 8 < 9 < 10 )
#pragma pack( 1 )
///1+8 = 9 —> 9
#pragma pack( 16 )
///1+8 = 9 —> 16( 16—>8 ---- 8 < 9 < 16 )
struct sample2
{
char a; ///1
int b; ///4
};
#pragma pack( 8 )
/// 1 + 4 = 5 —> 8( 8 —> 4 )
#pragma pack( 16 )
/// 1 + 4 = 5 —> 8( 16 —> 4 )
說明:#pragma pack告訴編譯器進行記憶體邊界對齊,一般都是採用編譯器的設定對整個項目採用同一對齊方案,而且通常為預設8位元組對齊。
/////////////////////////////////以下內容於 2005-12-10 添加/////////////////////////////////
今天又看到以前測試的一段代碼,突然不明白了起來,又稍寫了幾個測試。
struct sample3
{
char a; ///1
int b; ///4
char c; ///1
};
///default ///12
#pragma pack( 4 ) ///12
#pragma pack( 2 ) ///08
#pragma pack( 1 ) ///06
#pragma pack( 16 ) ///12
原來,其實編譯器,根據對齊指示的對齊位元組和最大成員的位元組,對每個成員進行了對齊:編譯器會取對齊指示和最大成員位元組中較小的一個用於補齊其它成員。那麼,上面的sample1/2/3也就都在情理之中了。為了證實這點,我們還再看一個例子:
struct sample4
{
char a; ///1
int b; ///4
double c; ///8
char d; ///1
};
///default: ///8+8+8+8 = 32
#pragma pack( 4 ) ///4+4+8+4 = 20
#pragma pack( 2 ) ///2+4+8+2 = 16
#pragma pack( 1 ) ///1+4+8+1 = 14
#pragma pack( 16 ) ///8+8+8+8 = 32
而實際上,編譯器給出的值是:24、20、16、14、24
那麼說明我錯了。注意一下,我發現char a,int b加起來也才5<8,難到編譯器進行了聯合對齊?
struct sample5
{
char a; ///1
double c; ///8
int b; ///4
char d; ///1
};
編譯器給出結果:24、20、16、14、24
這用聯合對齊的解釋正好符合,我又實驗了不同的資料,發現這個結論並不太準確確。於是,我輸出了每一個對象成員地址進行分析。由於實驗資料量很大,這裡就不列出了。
最後得到了以下結論:
1. 成員的對齊是按聲明順序進行的;
2. 對齊值由編譯指示和最大成員兩者較小的值決定;
3. 未對齊到對齊值的成員一起形成塊對齊(聯合對齊);
4. 上一個(下一個)對齊採用自己較大則不變,自己較小則填充自己對齊到上一個(下一個)大小;
5. 每成員對齊:如果前面已對齊到對齊值,下一個對齊自己。如果前面未對齊到對齊值,如果加上下一個成員不大於對齊值,下一個對齊自己,否則填充自己塊對齊到對齊值。
6. 最後還未對齊到對齊值的,填充空間塊對齊到對齊值。
從這些結論,可以得到:
1. 以上的對齊原則其實是盡量整齊排列、盡量節省記憶體。
2. 聲明成員應該盡量避免不同類型錯雜開來,最好採用從小到大或者從大到小的順序(錯開後,會因為上對齊和下對齊而增加填充開銷)。
3. 編譯器預設採用8位元組對齊主要是因為最大基本類型為8自己(以前自己不明白,在論壇提過問,後來,以為是SSE指令的原因)。
4. 手算sizeof是沒有必要的,負責的(可以先對齊出對齊塊,用塊數乘對齊值)。