1. 程式人生 > >C++裡的對齊規則

C++裡的對齊規則

什麼是對齊?
在C/C++中,資料結構或類的成員變數,並不是按照它們的大小,一個一個緊湊地排列在空間上的。它們是按照一種特定的方法排列的,有可能在兩個成員變數之間插入一個或個byte,以保證每個成員變數的起始位置是都是從某些特定的位置開始的。這就是對齊。單純從語言上描述對齊有些枯燥難以理解,下文將配以例子說明C/C++是基於什麼樣的規則對齊的。

為什麼要對齊?
在大多數平臺上,系統從某些特定的位置開始讀資料非常快,而從其它位置讀資料會慢很多。C/C++是一種注重效率的語言,為了使程式速度儘可能地快,選擇犧牲很少的空間,用填充byte的方式保證所有資料的儲存都從這些特定的位置開始,而達到較高的執行速度。

什麼是4位元組對齊?
當說到一個結構體n位元組對齊,包含2個資訊:
(1)結構體的起始地址能被n整除
(2)結構體的總大小能被n整除
當說到一個成員變數是n位元組對齊的,說明該變數的起始地址能被n整除。比如某變數的對齊位元組數是4,那麼它的首地址的16進位制形式一定是以0/4/8/C結尾的。
在實際場景中,n可以是1、2、4、8

規則開始啦。

1.在64位系統中,預設的對齊位元組是8。在32位系統中,網上查到的資料有說4也有說8,因為用的是64位系統,沒有驗證過32位的情況。

struct type
{
    int a;
    long b;
};

int main()
{
    type t;
    cout
<<"size = "<<sizeof(t)<<endl; cout<<"address a = "<<&t.a<<endl; cout<<"address b = "<<&t.b<<endl; return 0; }

執行結果:

size = 16
address a = 0x7fffa0b9e9d0
address b = 0x7fffa0b9e9d8

排列方式:
a a a a 0 0 0 0
b b b b b b b b

2.對於整個結構體而言,其整個大小也必須是位元組數的倍數,如果不足,後面補充

struct type
{
    long a;
    int b;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    return 0;
} 

執行結果:

size = 16
address a = 0x7ffff8bb0840
address b = 0x7ffff8bb0848

排列方式:
a a a a a a a a
b b b b 0 0 0 0

3.可以通過#pragma pack修改預設值,但只能改小,不能改大。

#pragma pack (4)

struct type
{
    long a;
    int b;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    return 0;
}

執行結果:

size = 12
address a = 0x7fffe365d4d0
address b = 0x7fffe365d4d8

排列方式:
a a a a
a a a a
b b b b

4.
對於結構體或類中的某一個成員變數而言,它的對齊按照 #pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。
a=資料成員自動長度
b=預設值,取值範圍為1,2,4,8
該資料成員對齊位元組=min(a,b)

struct type
{
    bool a;
    int b;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    return 0;
}

執行結果:

size = 8
address a = 0x7fff43062ed0
address b = 0x7fff43062ed4

排列方式:
a 0 0 0
b b b b

5.
對於一個結構體,它的對齊位元組取取決於結構中最大的成員與#pragma pack指定的數值
a=結構中最大的資料成員的自動長度
b=預設值,取值範圍為1,2,4,8
該結構體對齊位元組=min(a,b)

struct type
{
    int a;
    bool b;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    return 0;
}

執行結果:

size = 8
address a = 0x7fff518d64a0
address b = 0x7fff518d64a4

排列方式:
a a a a
b 0 0 0

6.
預設值等於或超過所有資料成員長度的時候,預設值的大小將不產生任何效果。
a=max(所有資料成員長度)
b=預設值,取值範圍為1,2,4,8
If(b>a)
該結構體對齊位元組數為a

7.結構體包含結構體的情況
若某一成員變數是另一個結構體,該成員變數的對齊位元組數為成員變數結構體的對齊位元組數
例1:

struct byte3
{
    char a;
    char b;
    char c;
};

struct type
{
    byte3 a;
    byte3 b;
    byte3 c;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    cout<<"address c = "<<&t.c<<endl;
    return 0;
}

執行結果

size = 9
address a = 0x7fff6346cdf0
address b = 0x7fff6346cdf3
address c = 0x7fff6346cdf6

說明:
由於所有結構體中最大的位元組長度是1,所以是1位元組對齊的。雖然有一個結構體的大小是3,但是還是按照1對齊的。
例2:

struct byte3
{
    short a;
    char b;
};

struct type
{
    byte3 a;
    byte3 b;
    byte3 c;
};

int main()
{
    type t;
    cout<<"size = "<<sizeof(t)<<endl;
    cout<<"address a = "<<&t.a<<endl;
    cout<<"address b = "<<&t.b<<endl;
    cout<<"address c = "<<&t.c<<endl;
    return 0;
}

執行結果:

size = 12
address a = 0x7fffff3f1ef0
address b = 0x7fffff3f1ef4
address c = 0x7fffff3f1ef8

說明:
由於所有結構體中最大的位元組長度是1,所以結構體之間是2位元組對齊的。