C語言內存對齊詳解(1)

1、什麼是字節對齊,爲何要對齊?編程

    現代計算機中內存空間都是按照byte劃分的,從理論上講彷佛對任何類型的變量的訪問能夠從任何地址開始,但實際狀況是在訪問特定類型變量的時候常常在特定的內存地址訪問,這就須要各類類型數據按照必定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。架構

    對齊的做用和緣由:各個硬件平臺對存儲空間的處理上有很大的不一樣。一些平臺對某些特定類型的數據只能從某些特定地址開始存取。好比有些架構的CPU在訪問一個沒有進行對齊的變量的時候會發生錯誤,那麼在這種架構下編程必須保證字節對齊.其餘平臺可能沒有這種狀況,可是最多見的是若是不按照適合其平臺要求對數據存放進行對齊,會在存取效率上帶來損失。好比有些平臺每次讀都是從偶地址開始,若是一個int型(假設爲32位系統)若是存放在偶地址開始的地方,那 麼一個讀週期就能夠讀出這32bit,而若是存放在奇地址開始的地方,就須要2個讀週期,並對兩次讀出的結果的高低字節進行拼湊才能獲得該32bit數據。顯然在讀取效率上降低不少。ide

2、請看下面的結構:測試

struct struct1 
{ 
   double dda; 
   char cda; 
   int ida; 
}; 

sizeof(struct1) = ?
錯誤的求法:spa

sizeof(struct1)=sizeof(double)+sizeof(char)+sizeof(int)=13code

可是當你在VC上運行以下測試代碼:blog

#include<stdio.h>

struct mystruct
{
    double dda;
    char cda;
    int ida;
};

int main()
{
    struct mystruct ss;
    printf("%d\n",sizeof(ss));
    return 0;
}
C代碼

運行結果爲:16內存


其實,這是VC對變量存儲的一個特殊處理。爲了提升CPU的存儲速度,VC對一些變量的起始地址作了「對齊」處理。在默認狀況下,VC規定各成員變量存放的起始地址相對於結構的起始地址的偏移量必須爲該變量的類型所佔用的字節數的倍數。下面列出經常使用類型的對齊方式(vc6.0,32位系統)ci

類型            對齊方式(變量存放的起始地址相對於結構的起始地址的偏移量)it

char              偏移量必須爲sizeof(char)即1的倍數

int               偏移量必須爲sizeof(int)即4的倍數

float             偏移量必須爲sizeof(float)即4的倍數

double            偏移量必須爲sizeof(double)即8的倍數

Short             偏移量必須爲sizeof(short)即2的倍數


各成員變量在存放的時候根據在結構中出現的順序依次申請空間,同時按照上面的對齊方式調整位置,空缺的字節VC會自動填充。同時VC爲了確保結構的大小爲結構的字節邊界數(即該結構中佔用最大空間的類型所佔用的字節數)的倍數,因此在爲最後一個成員變量申請空間後,還會根據須要自動填充空缺的字節。

如今來分析VC是怎樣來存放結構的:

struct struct1 
{ 
   double dda; 
   char cda; 
   int ida; 
}; 

第一個成員dda分配空間,其起始地址跟結構的起始地址相同(恰好偏移量0恰好爲sizeof(double)的倍數),該成員變量佔用sizeof(double)=8個字節;接下來爲第二個成員cda分配空間,這時下一個能夠分配的地址對於結構的起始地址的偏移量爲8,是sizeof(char)的倍數,因此把cda存放在偏移量爲8的地方知足對齊方式,該成員變量佔用 sizeof(char)=1個字節;接下來爲第三個成員ida分配空間,這時下一個能夠分配的地址對於結構的起始地址的偏移量爲9,不是sizeof (int)=4的倍數,爲了知足對齊方式對偏移量的約束問題,VC自動填充3個字節(這三個字節沒有放什麼東西),這時下一個能夠分配的地址對於結構的起始地址的偏移量爲12,恰好是sizeof(int)=4的倍數,因此把ida存放在偏移量爲12的地方,該成員變量佔用sizeof(int)=4個字節;這時整個結構的成員變量已經都分配了空間,總的佔用的空間大小爲:8+1+3+4=16,恰好爲結構的字節邊界數(即結構中佔用最大空間的類型所佔用的字節數sizeof(double)=8)的倍數,因此沒有空缺的字節須要填充。因此整個結構的大小爲:sizeof(struct1)=8+1+ 3+4=16,其中有3個字節是VC自動填充的,沒有聽任何有意義的東西。

下面再舉個例子,交換一下上面的struct1的成員變量的位置,使它變成下面的狀況:

struct mystruct2
{
    char cda;
    double dda;
    int ida;
};

在VC環境下,運行結果爲:24

struct mystruct2
{
    char cda;    //偏移量爲0,知足對齊方式,cda佔用1個字節;
    double dda;  //下一個可用的地址的偏移量爲1,不是sizeof(double)=8 
                 //的倍數,須要補足7個字節才能使偏移量變爲8(知足對齊 
                 //方式),所以VC自動填充7個字節,dda存放在偏移量爲8 
                 //的地址上,它佔用8個字節。 

    int ida;     //下一個可用的地址的偏移量爲16,是sizeof(int)=4的倍 
                 //數,知足int的對齊方式,因此不須要VC自動填充,type存 
                 //放在偏移量爲16的地址上,它佔用4個字節。
   
   //全部成員變量都分配了空間,空間總的大小爲1+7+8+4=20,不是結構 
   //的節邊界數(即結構中佔用最大空間的類型所佔用的字節數sizeof 
   //(double)=8)的倍數,因此須要填充4個字節,以知足結構的大小爲 
   //sizeof(double)=8的倍數。
};

因此該結構總的大小爲:sizeof(struct2)爲1+7+8+4+4=24。其中總的有7+4=11個字節是VC自動填充的,沒有聽任何有意義的東西。

相關文章
相關標籤/搜索