1. 程式人生 > >結構體的對齊和補齊

結構體的對齊和補齊

首先我們先看看下面的C語言的結構體:

typedef struct MemAlign

{

  1. int a;

  2. char b[3];

  3. int c;

}MemAlign

 以上這個結構體佔用記憶體多少空間呢?也許你會說,這個簡單,計算每個型別的大小,將它們相加就行了,以32為平臺為例,int型別佔4位元組,char佔用1位元組,所以:4 + 3 + 4 = 11,那麼這個結構體一共佔用11位元組空間。好吧,那麼我們就用實踐來證明是否正確,我們用sizeof運算子來求出這個結構體佔用記憶體空間大小,sizeof(MemAlign),出乎意料的是,結果居然為12?看來我們錯了?當然不是,而是這個結構體被優化了,這個優化有個另外一個名字叫“對齊”.


    那麼,記憶體對齊有哪些原則呢?我總結了一下大致分為三條:
第一條:第一個成員的首地址為0
第二條:每個成員的首地址是自身大小的整數倍
       第二條補充:以4位元組對齊為例,如果自身大小大於4位元組,都以4位元組整數倍為基準對齊。
第三條:最後以結構總體對齊。
        第三條補充:以4位元組對齊為例,取結構體中最大成員型別倍數,如果超過4位元組,都以4位元組整數倍為基準對齊。(其中這一條還有個名字叫:“補齊”,補齊的目的就是多個結構變數挨著擺放的時候也滿足對齊的要求。)
 

#pragma pack(4)
typedef struct MemAlign
{
    char a[18];
    double b;    
    char c;
    int d;    
    short e;    
}MemAlign;
 

我們就以這個圖來講解是如何對齊的:
第一個成員(char a[18]):首先,假設我們把它放到記憶體開始地址為0的位置,由於第一個成員佔18個位元組,所以第一個成員佔用記憶體地址範圍為0~18。
第二個成員(double b):由於double型別佔8位元組,又因為8位元組大於4位元組,所以就以4位元組對齊為基準。由於第一個成員結束地址為18,那麼地址18並不是4的整數倍,我們需要再加2個位元組,也就是從地址20開始擺放第二個成員。
第三個成員(char c):由於char型別佔1位元組,任意地址是1位元組的整數倍,所以我們就直接將其擺放到緊接第二個成員之後即可。
第四個成員(int d):由於int型別佔4位元組,但是地址29並不是4的整數倍,所以我們需要再加3個位元組,也就是從地址32開始擺放這個成員。
第五個成員(short e):由於short型別佔2位元組,地址36正好是2的整數倍,這樣我們就可以直接擺放,無需填充位元組,緊跟其後即可。
    這樣我們記憶體對齊就完成了。但是離成功還差那麼一步,那是什麼呢?對,是對整個結構體補齊,接下來我們就補齊整個結構體。那麼,先讓我們回顧一下補齊的原則:“以4位元組對齊為例,取結構體中最大成員型別倍數,如果超過4位元組,都以4位元組整數倍為基準對齊。”在這個結構體中最大型別為double型別(佔8位元組),又由於8位元組大於4字 節,所以我們還是以4位元組補齊為基準,整個結構體結束地址為38,而地址38並不是4的整數倍,所以我們還需要加額外2個位元組來填充結構體。

總結:

 結構體位元組對齊的細節和具體編譯器實現相關,但一般而言滿足三個準則:

     1) 結構體變數的首地址能夠被其最寬基本型別成員的大小所整除;

     2) 結構體每個成員相對結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);

     3) 結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組{trailing padding}。

 

對齊位元組可以用:#pragma pack(4) 指定