下面的代碼片段定義了結構體A和B:
在32位機器上,char、short、int三種類型的大小分別是1、2、4。那麼上面兩個結構體的大小如何呢?
結構體A中包含了一個4字節的int,一個1字節的char和一個2字節的short,B也一樣,所以A、B的大小應該都是4+2+1 = 7字節。但是,實驗給出的卻是另外的結果:
其原因還要從字節對齊說起。
現代計算機中內存空間都是按照字節來劃分的,從理論上來講,對變量的訪問可以從任何地址開始;但在實際情況中,爲了提升存取效率,各類型數據需要按照一定的規則在空間上排列,這使得對某些特定類型的數據只能從某些特定地址開始存取,以空間換取時間,這就是字節對齊。
結構體默認的字節對齊一般滿足三個準則:
(1)結構體變量的首地址能夠被其最寬基本類型成員的大小所整除。
(2)結構體每個成員相對於結構體首地址的偏移量(offset)都是成員自身大小的整數倍,如有需要,編譯器會在成員之間加上填充字節(Internal Adding)。
(3)結構體的總大小爲結構體最寬基本類型成員大小的整數倍,如有需要,編譯器會在最末一個成員之後加上填充字節(Trailing Padding)。
按照這三條規則再去分析結構體A和B,就不會對於上述的結果一臉詫異了。這兩個結構體在內存空間中的排列如圖1-2所示(灰色網格表示的字節爲填充字節)。
圖1-2 結構體A和B的內存分佈 |
在編程應用中,如果空間緊張,需要考慮節約空間,那麼就需要將結構體中的各個變量按照上面的原則進行排列。基本的原則是:把結構體中的變量按照類型大小從小到大依次聲明,儘量減少中間的填充字節。
也可以採用保留字節的形式顯式地進行字節填充實現對齊,以提高存取效率。其實這就是時間與空間的博弈。如下面的代碼片段所示,其中的reserved成員對程序沒有什麼意義,它只是填補空間以達到字節對齊的目的:
在某些時候,還可以通過編譯器的pack指令調整結構體的對齊方式。#pragma pack的基本用法爲:
將結構體A的對齊方式設爲1字節對齊,那麼A就不再有填充字節了,sizeof(A)的結果即爲各元素所佔字節之和7。
請記住:
瞭解結構體中元素的對齊規則,合理地爲結構體元素進行佈局。這樣不僅可以有效地節約空間,還可以提高元素的存取效率。