結構體成員對齊的問題

鴨脖發表於2013-01-02

 現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

(1)現象

      比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。顯然在讀取效率上下降很多。看下面一個例子:

32位機上,預設4位元組對齊

struct A

{

     int a;

     char b;

     short c;

};

struct B

{

     char b;

     int  a;

     short c;

};

main()

{

     struct A x;

     struct B y;

     printf("%d %d", sizeof(x),sizeof(y));

}

得到的結果是8,12。按理說兩者應該一樣是7才對?之所以出現上面的結果是因為編譯器要對資料成員在空間上進行對齊。對A,a佔4位元組,b佔一位元組,c佔兩位元組,b、c可以放在一個4位元組裡面,且最小分配是4,所以共佔8;對B,b佔一位元組,但它容納不了a了,所以a要佔另外4位元組,b只能單佔4位元組,後面的c也只能單佔4位元組,也即沒有共用的,所以佔12。可見次序是有影響的。

      上面是按照編譯器的預設設定進行對齊的結果,那麼我們是不是可以改變編譯器的這種預設對齊設定呢?當然可以。

#pragma pack(2)
struct  B
{
      char b;
      int a;
      short c;
};

int _tmain()
{
       B y;
       printf("%d ",sizeof(y));
}

輸出是8,因為是2位元組對齊最小可以2位元組分配,b佔兩位元組,a佔4位元組,c佔2位元組。類似的還有如下:

#pragma pack(1)
struct  B
{
      char b;
      int a;
      short c;
};

int _tmain()
{
       B y;
       printf("%d ",sizeof(y));
}

輸出是7,因為1位元組對齊,最小可以分配1。那麼b佔一位元組,a佔4位元組,c佔2位元組。

(2)原則歸納

      綜合如下,對齊的原則:
1,資料型別自身的對齊值:對於char型資料,其自身對齊值為1,對於short型為2,對於int,float,double型別,其自身對齊值為4,單位位元組。
2,結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。
3,指定對齊值:#pragma pack (value)時的指定對齊值value。

(3)如何使用
      如何修改編譯器的預設對齊值?

1,在VC IDE中,可以這樣修改:[Project]|[Settings],c/c++選項卡Category的Code Generation選項的Struct Member Alignment中修改,預設是8位元組。

2,在編碼時,可以這樣動態修改:#pragma pack 。

3,如果在程式設計的時候要考慮節約空間的話,那麼我們只需要假定結構的首地址是0,然後各個變數按照上面的原則進行排列即可。基本的原則就是把結構中的變數按照型別大小從小到大宣告,儘量減少中間的填補空間。還有一種就是為了以空間換取時間的效率,比如:有一種使用空間換時間做法是顯式的插入reserved成員:
         struct A{
           char a;
           char reserved[3];//使用空間換時間
           int b;
   }
reserved成員對我們的程式沒有什麼意義,它只是起到填補空間以達到位元組對齊的目的,當然即使不加這個成員通常編譯器也會給我們自動填補對齊,我們自己加上它只是起到顯式的提醒作用。


轉自http://blog.csdn.net/sky0829/article/details/6010482

相關文章