C 內存對齊

C 內存對齊

對於程序員來說,最煩惱最耗時的工作莫過於與內存打交道,但是內存至關重要,不得不對其保持最大的警惕。

爲什麼需要內存對齊?

  • 平臺原因:不是所有的一歐諾個見平臺都能訪問任意地址上的任意數據的;某些硬件平臺如果訪問未對齊的地址,則會報出對齊錯誤,有些專業的處理器通常不支持訪問未對齊地址。
  • 性能原因:數據結構(尤其是棧)應該儘可能地在自然邊界對其。原因在於,爲了訪問未對齊的內存,處理器需要做兩次內存訪問,而對齊的內存訪問僅需要一次。

計算機通常以字大小的塊來處理內存,一個字是計算機數據的自然單位,通常由計算機架構決定。現代通用計算機通常是4個字節(32位)或8個字節(64位)。

假設處理器,一次讀取4個字節的數據。且內存如圖所示

memory_alignment_initial

若保存4字節的int類型,不需要進行額外工作就可以正確對齊,因爲int數據類型大小與該架構的數據自然單元契合。

memory_alignment_int_in_memory

若我們放置一個charmemory_alignment_char, 一個shortmemory_alignment_short, 和一個intmemory_alignment_int到內存中。原本應該得到的結果如下所示

memory_alignment_char_short_int

這將需要兩次內存訪問,並且進行移位來獲取int數據。這將比內存對齊的數據至少花費兩倍時間,因爲計算機科學家們提出了內存對齊的方法。在本例中,添加一些padding在第一個字節,確保有效對齊

memory_alignment_char_short_int_alignment
上圖所示被認爲是自然對齊,編譯器會自動添加正確的padding根據目標平臺。

內存對齊規則

默認對齊

如果有指定對齊字節數目,則編譯器會按 類或結構中最大類型長度來對齊。可以通過語句#pragma pack(i)來指定對齊字節數目,i的取值爲1, 2, 4, 8, 16

對齊規則:

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

示例

Example 1

未指定對其字節,則n = 4即最大成員int的大小

struct Foo{
    char x;  // 1 byte  放在偏移爲0的地址,位置區間爲[0]
    short y;  // 2 bytes  起始位置應該是2的倍數,即2, 位置區間爲[2, 3]
    int z;  // 4 bytes  起始位置應爲4的倍數,位置區間爲[4, 7]
};

此時成員共佔用[0-7] 8個字節,還需整體對齊,大小應該是4的倍數,即8

Example 2

假設指定對其字節爲8, 那麼n = min(8, 8) = 8

struct Foo{
    char x;  // 1 byte 起始位置爲0, 位置區間爲[0]
    double y;  // 8 bytes 起始位置應爲8的倍數,即8, 位置區間爲[8, 15]
    char z;  // 1 byte 起始位置應爲1的倍數,即16, 位置區間爲[16]
}

此時成員共佔用17字節,還要整體對齊,爲8的倍數,故大小爲24

參考

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