1. 程式人生 > >經典C語言面試題4:位元組對齊的作用

經典C語言面試題4:位元組對齊的作用

   一、什麼是位元組對齊? 

    在現代計算機中,記憶體空間都是按照字節(byte)劃分的。從理論上講對任何型別的變數的訪問可以從任何地址開始,但實際情況是,訪問特定型別的變數的時候經常在特定的記憶體地址訪問,這就需要各種型別的資料按照一定規則在空間上排列,而不是順序地一個接一個地排放,這種所謂的規則就是位元組對齊。這麼長一段話的意思是說:位元組對齊可以提升存取效率,也就是用空間換時間。

例如:

struct A
{
   char a;
   char reserved[3];//使用空間換時間
   int b; 
};


注:其中的reserved成員對於程式並沒有意義,只是起到了填補空間達到位元組對齊的目的。當然,即使不加這個成員,編譯器也會自動為我們補齊,加上它只是起到顯式提醒。

二、為什麼需要位元組對齊?

       因為各個硬體平臺對儲存空間的處理上有很大的不同,一些平臺對某些特定型別的資料只能從某些特定地址開始存取。比如有些架構的CPU在訪問一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對資料存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。顯然在讀取效率上下降很多。

三、幾個基本概念

1、基本資料型別的自身對齊值

      例如,char型資料的自身對齊值為1位元組,short型別自身對齊值為2位元組,int、float、long型別自身對齊值均為4位元組,double型別自身對齊值均為8位元組。(32位系統)

2、結構體或類的自身對齊值

     其成員中自身對齊值最大的那個值。

3、指定對齊值

      通過預編譯指令 #pragma pack (value) 來指定的對齊值value。(注:取消自定義對齊值得指令為 #pragma pack ( ))

4、資料成員、結構體和類的有效對齊值

     其自身對齊值和指定對齊值中較小的那個值。

四、位元組對齊的幾個例子淺析

例1:設有如下兩個結構體

struct A
{
  char a;
  short b;
  int c;
};
struct B
{
  short b;
  int c;
  char a;
};

那麼上面兩個結構體的大小是多少呢?

對於結構體A:a是char型資料,佔用1位元組記憶體;short型資料,佔用2位元組記憶體;int型資料,佔用4位元組記憶體。因此,結構體A的自身對齊值為4,sizeof(struct A) =8位元組。由於結構體型別資料是按順序儲存結構一個接一個向後排列的,於是其儲存方式為:


為了更加明顯地表示“對齊”,我們可以將以上結構想象為以下的行排列:


對於結構體B:同理也是4位元組對齊,但是sizeof(struct B) =12位元組。


想象為以下的行排列:


例2:為結構體指定對齊值

#pragma pack(2) //指定2位元組對齊
struct C
{
  char a;
  int b;
  short c;
};
#pragma pack() //取消指定對齊,恢復預設對齊

對於結構體C:由於其自身對齊值為4位元組(int b),而指定對齊值為2位元組,因此該結構體的有效對齊值為較小的2位元組,那麼sizeof(struct C) = 8 位元組。


#pragma pack(1) //指定1位元組對齊
struct D
{
  char a;
  int b;
  short c;
};
#pragma pack() //取消指定對齊,恢復預設對齊

對於結構體D:同理可知,由於其自身對齊值為4位元組(int b),而指定對齊值為1位元組,因此該結構體的有效對齊值為較小的1位元組,那麼sizeof(struct D) = 7 位元組。

五、總結

由以上分析可知,位元組對齊會造成空間上的浪費。 事實上,除了結構體之外,整個程式在給每個變數進行記憶體分配時都會遵循對齊機制,也都會產生記憶體空間的浪費。但我們要知道,這種浪費是值得的,因為它換來的是效率的提高。