1. 程式人生 > >淺談C語言中的記憶體對齊

淺談C語言中的記憶體對齊

先看一下下面兩段程式碼:

1:#include

int main(){

      struct st{

             short a;

             int b,c;

             char d;

      };

      printf("%d",sizeof(struct st));

      return 0;:

}

2:#include

int main(){

      struct st{

             int b,c;

             short a;

             char d;

      };

      printf("%d",sizeof(struct st));

      return 0;

}

可以看出,上面兩段程式碼之間唯一的區別就是第四行和第五行調換了位置。程式碼的結果都是輸出目標結構體所佔的記憶體空間。我們都知道在不同系統中,同一資料型別所佔位元組數也不同。實驗所用系統預設為short2,int4,char1。那麼,上面兩段程式碼的執行結果是什麼呢?第一段程式碼結果為16,第二段結果為12。

兩個結構體均由兩個int,一個char和一個short組成,但結果並不是簡單記憶體相加所得的11。而且僅僅調換結構體內部的程式碼順序後,結構體的大小也隨之改變。這正是由於C語言自然啟用的記憶體對齊造成的。

當我們設定一個變數後,系統便會為他分配記憶體空間。Int就給4個,char就給一個,看起來很合理。但實際上,再分配記憶體空間時,系統還要考慮到定址速度。系統的取值有一個特點,即是對一個變數的取值一次只會從該變數的記憶體大小的整數倍記憶體地址處開始取值,一次無法取完就兩次。比如一個int型別的資料,起始地址為2,佔用了2、3、4、5。那麼當系統對他定址時每次查詢4個單位記憶體。第一次就會從0開始取值,取值為0、1、2、3的內容,0和1的內容並非想要的就會捨去,然後儲存下2和3的內容。第二次取值會取4、5、6、7的內容,同樣只保留4和5的內容。然後在兩次取值後再把內容合併得到你想要的int內容。

上面可以看出,在沒有記憶體對齊時,由於特殊的定址方法,取值需要兩次。然而如果一開始就把這個int資料儲存在0位或者4位(其他像8、12、16等4的整數倍處也可),便可以在第一次取值時,就取到整個int的內容。記憶體對齊便是如此原理。既不會降低取值速度,也不影響快速定址。但事情沒有十全十美的。正如我們剛剛所看到的,在結構體中,由於記憶體對齊的存在,原本大小為11的結構體,變成了12和16。

為什麼會這樣子呢?因為一個結構體在系統中所佔記憶體為連續的。而一個結構體中未免不會出現多種資料型別。當多個數據型別在同一結構體中都需要記憶體對齊時,在每個資料型別中間就未免會出現一些空的記憶體位。但由於這些空記憶體位處於結構體之中,就也會被包含到結構體的記憶體佔用當中。

再看一下上面兩段程式碼:

第一段的順序是short int int char

Short所佔2個單位記憶體,即可以假設為0和1,第一個int佔四個,由於記憶體對齊,他只能從4開始,也就是4567。第二個int就是8、9、10、11。最後char則是佔12。到這為止,該結構體所佔記憶體僅為13,但為何系統反饋結果為16呢?那是因為結構體本身也要存在對齊。結構體的長度必須為結構體中最長的資料型別長度的整數倍。第一個結構體中最長的資料型別是int,長度為4,所以該結構體長度需湊到4的整數倍,即為16。

依照上面的方法,也可以推算出結構體二的長度為12。

幾乎同樣的結構體,卻僅因程式碼順序記憶體大小相差25%。可以見得合理利用記憶體對齊是多麼重要,程式碼順序也是會在很大程度上影響程式質量的。

有時,我們可能不需要如此快的執行速度,而相對的更需要較小的佔用記憶體空間。這時,我們可以自己定義記憶體對齊的對齊值(如何設定自己百度︿( ̄︶ ̄)︿方法有很多,這裡就不在細講)。通過干涉記憶體對齊,便可以以犧牲速度為代價,獲得更小的記憶體佔用。