【C++】內存對齊

【參考原文】JustDoIT:C++ 內存對齊

C++ 內存對齊

注:本文代碼測試環境爲win7 X64 cpu, 編譯器爲gcc4.7.1 和 vs2010

內存對齊是編譯器爲了便於CPU快速訪問而採用的一項技術

我們先從一個例子開始,對下面的類(或者結構體)

class node
{
    char c;

    int i;

    short s;
}no;

sizeof(no)的值是多少呢,如果你的回答是7(1+4+2),那麼你應該認真閱讀下面的內容。可以在編譯器上試試,輸出的結果是12,這就是內存對齊的結果。

爲什麼要進行內存對齊呢?

  1. 平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
  2. 性能原因:數據結構(尤其是棧)應該儘可能地在自然邊界上對齊。原因在於,爲了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。

編譯器一般按照幾個字節對齊呢?本文中兩個編譯器默認按照類中最大類型長度來對齊,我麼也可以使用語句#pragma pack(i)(i = 1,2,4,8,16)來設置對齊字節數目,vs還可以在項目屬性-配置屬性-c/c++-代碼生成-結構成員對齊設置。

對齊規則

  1. 如果設置了內存對齊爲 i 字節,類中最大成員對齊字節數爲j,那麼整體對齊字節n = min(i, j) (某個成員的對齊字節數定義:如果該成員是c++自帶類型如int、char、double等,那麼其對齊字節數=該類型在內存中所佔的字節數;如果該成員是自定義類型如某個class或者struct,那個它的對齊字節數 = 該類型內最大的成員對齊字節數《詳見實例4》)
  2. 每個成員對齊規則:類中第一個數據成員放在offset爲0的位置;對於其他的數據成員(假設該數據成員對齊字節數爲k),他們放置的起始位置offset應該是 min(k, n) 的整數倍
  3. 整體對齊規則:最後整個類的大小應該是n的整數倍
  4. 當設置的對齊字節數大於類中最大成員對齊字節數時,這個設置實際上不產生任何效果(實例2);當設置對齊字節數爲1時,類的大小就是簡單的把所有成員大小相加

實例分析

我們通過以下幾個實例來分析

實例1:(沒有指定對齊字節,則n = 最大成員(int i)的大小4)

class node
{
    char c;   //放在位置0,位置區間[0]

    int i;      //4 = n, 那麼放置起始位置應該是4的倍數,即4,位置區間爲[4~7]

    short s; //2 < n,那麼放置起始位置應該是2的倍數,即8,位置區間爲[8~9]
}no;

此時成員共佔用[0~9]10個字節,還要整體對齊,大小應該是4的倍數,即12

實例2:(假設指定對齊字節爲8,那麼n = min(8,4) = 4)

class node
{
    int i; //放在位置0,位置區間[0~3]

    char c; //1 < n, 那麼放置起始位置應該是1的倍數,即4,位置區間爲[4]

    short s; //2 < n,那麼放置起始位置應該是2的倍數,即6,位置區間爲[6~7]
}no;

成員共佔據[0~7]8個字節,剛好是4的倍數,因此大小是8
實例3:(假設指定對齊字節是2,則n = min(2,4) = 2)

class node
{
    char c; //放在位置0,位置區間[0]

    int i; //4 > n, 那麼放置起始位置應該是2的倍數,即2,位置區間爲[2~5]

    short s; //2 = n,那麼放置起始位置應該是2的倍數,即6,位置區間爲[6~7]
}no;

此時成員共佔用[0~7]8個字節,剛好是4的倍數,因此大小是8

實例4:(按照默認設置)

class temp 
{ 
    char c; 
    int i; 
    short s1; 
};

由實例1可知,默認對齊情況下,temp的大小是12,temp的對齊字節數是:三個成員取最大的,即爲4;

對於node,n = 其三個成員對齊字節數取最大,即等於t的對齊字節數,也就是 4。

class node
{
    char c; //放在位置0,位置區間[0]

    temp t; //4(temp的對齊字節數) = n, 那麼放置起始位置應該是4的倍數,即4,位置區間爲[4~15]

    short s; //2 < n,那麼放置起始位置應該是2的倍數,即16,位置區間爲[16~17]
}no;

此時成員共佔用[0~17]18個字節,還要整體對齊,大小應該是4的倍數,因此大小是20

實例5:(默然設置)

對於node,n = 其三個成員對齊字節數取最大,即等於d的對齊字節數,也就是 8。

class node
{
    temp t; //放在位置0,位置區間[0~11]

    double d; //8(temp的對齊字節數) = n, 那麼放置起始位置應該是8的倍數,即16,位置區間爲[16~23]

    short s; //2 < n,那麼放置起始位置應該是2的倍數,即24,位置區間爲[24~25]
}no;

此時成員共佔用[0~25]26個字節,還要整體對齊,大小應該是8的倍數,因此大小是32.

類繼承時的內存對齊

class A
{
    int i;
    char c1;
}

class B:public A
{
    char c2;
}

class C:public B
{
    char c3;
}

sizeof(C)結果是多少呢,gcc和vs給出了不同的結果,分別是8、16
gcc中:C相當於把所有成員i、c1、c2、c3當作是在一個class內部,(先繼承後對齊)
vs中:對於A,對齊後其大小是8;對於B,c2加上對齊後的A的大小是9,對齊後就是12;對於C,c3加上對齊後的B大小是13,再對齊就是16 (先對齊後繼承)
關於c++對象繼承後的內存佈局,更詳細的分析可以《深度探索參考c++對象模型》第三章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章