1. 程式人生 > >c/c++ struct 記憶體對齊

c/c++ struct 記憶體對齊

為了讓CPU能夠更舒服地訪問到變數,struct中的各成員變數的儲存地址有一套對齊的機制。這個機制概括起來有兩點:第一,每個成員變數的首地址,必須是它的型別的對齊值的整數倍,如果不滿足,它與前一個成員變數之間要填充(padding)一些無意義的位元組來滿足;第二,整個struct的大小,必須是該struct中所有成員的型別中對齊值最大者的整數倍,如果不滿足,在最後一個成員後面填充。

各種型別的變數的align值如下,參考的是wikipedia的頁面:Data structure alignment

The following typical alignments are valid for compilers from 

MicrosoftBorland, and GNU when compiling for 32-bit x86:

  • char (one byte) will be 1-byte aligned.
  • short (two bytes) will be 2-byte aligned.
  • An int (four bytes) will be 4-byte aligned.
  • float (four bytes) will be 4-byte aligned.
  • double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux.
  • long double (twelve bytes) will be 4-byte aligned on Linux.
  • Any pointer (four bytes) will be 4-byte aligned on Linux. (eg: char*, int*)

The only notable difference in alignment for a 64-bit linux system when compared to a 32 bit is:

  • double (eight bytes) will be 8-byte aligned.
  • long double (Sixteen bytes) will be 16-byte aligned.
  • Any pointer (eight bytes) will be 8-byte aligned.

這裡寫了個程式來驗證這些事:

在64位linux下面執行這段程式碼的結果是: #include <stdio.h>


struct s {
    char a;
    short b;
    char c;
    double d;
    char e;
};


int main() {


    struct s s1;


    printf("%d, %d, %d, %d, %d\n",
        (char*)(&s1.a) - (char*)(&s1),
        (char*)(&s1.b) - (char*)(&s1),
        (char*)(&s1.c) - (char*)(&s1),
        (char*)(&s1.d) - (char*)(&s1),
        (char*)(&s1.e) - (char*)(&s1));
    printf("%d\n", sizeof(struct s));


    return 0;
}
0, 2, 4, 8, 16 24

由於對齊機制的存在,實際上上面的struct在記憶體中是長這個樣子的,共計24個位元組:

struct s {
    char a;             //在地址為0的位置
    char padding1[1];   //由於下面一個元素是short,對齊位元組數為2的位數,需要補1位元組
    short b;            //對齊到了地址為2的位置
    char c;             //在地址為4的位置
    char padding2[3];   //由於下面一個元素是double,對齊位元組數為8的倍數,需要補3位元組
    double d;           //對齊到了地址為8的位置
    char e;             //在地址為16的位置
    char padding3[7];   //整個struct的大小需要是對齊數最大者,也就是double的8位元組的整數倍
};

如果是在32位的linux下跑上面的程式,由於double的長度還是8位元組,但是對齊是4位元組的了,所以前面幾個成員的位置不變,而最後的padding只需要補3個位元組就可以了,所以輸出的結果是0, 2, 4, 8, 16及20.

對於windows,其32位和64位下double都是8位元組對齊的,所以在32位和64位下跑這個程式結果都是0, 2, 4, 8, 16及24.

最後,整個struct的大小的要求是對齊值最大者的整數倍,沒有什麼預設的4或者8的倍數一說。如果把上面程式中的a,b,c,d,e的型別全變成char,那麼最後的他們的地址會是0,1,2,3,4,整個struct的大小 sizeof(struct s)的值是5,沒有任何padding發生。

以上程式實驗的環境在64位centos x64上的gcc 4.1.2(32位結果加-m32引數)及Visual Studio 2008上得出。