1. 程式人生 > >c語言記憶體對齊與#pragma pack(n)

c語言記憶體對齊與#pragma pack(n)

一、什麼是記憶體對齊,為什麼要記憶體對齊 

    • 現在計算機記憶體空間都是按照byte位元組劃分的,理論上講對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體地址上訪問,這就需要各種資料型別按照一定的規則在空間上排列,而不是一個接一個的排放,這就是記憶體對齊。
    • cpu對記憶體的讀取不是連續的而是分塊讀取的,塊的大小隻能是2i個位元組數,從cpu的讀取效能和效率來考慮,若讀取的資料未對齊,則需要兩次匯流排週期來訪問記憶體,因而效率會大打折扣
    • 另外某些固定的硬體平臺只能從規定的相對地址處讀取特定型別的資料,否則會產生硬體異常。
    • 如果不按照適合平臺要求對資料存放進行對齊,會存在效率上的損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(32位系統)存放在偶地址開始的地方,那麼一個週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要兩個讀週期,並對兩次讀出結果的高低位元組進行拼湊才能得到該32bit資料。顯然在讀取效率上下降很多。

二、記憶體對齊規則

  • 在不用#pagrama pack()包裹的情況下,結構體或聯合體按照編譯器預設的對齊方式有以下三個對其原則:
    • 資料成員對齊原則:結構(struct或union)的資料成員,第一個資料成員存放在offset為0的地方,以後每個資料成員儲存的起始位置都要從該成員佔用記憶體大小的整數倍開始
    • 結構體作為成員的原則:如果一個結構中有某些結構體成員,則結構的成員要從其內部最大元素大小的整數倍地址開始儲存。(struct a裡有struct b,b裡有char,int,double等元素,那b應該從8的整數倍開始)
    • 結構(或聯合)的整體對齊原則:在資料成員各自對齊後,結構(或聯合)本身也要進行對齊,即以結構體內部佔用記憶體空間最大的資料型別進行對齊。(等同於sizeof該結構體的結果必須是其內部最大成員佔用記憶體的整數倍)
      • 1 struct mystruct
        2 {
        3 char a;//偏移量為0;a佔用一個位元組
        4
        double b;//下一個可用地址偏移量為1,不是sizeof(double)=8的整數倍,需要補7個位元組 5 int c;//下一個可用地址偏移量為1+7+8=16,是sizeof(int)=4的整數倍,滿足Int的對齊方式 6 }//所有成員變數都分配了空間,空間大小=1+7+8+4=20,不是最大空間型別double的整數倍,所以需要填充4個位元組以滿足結構體大小為sizeof(double)=8的整數倍 7 sizeof(mystruct)=24;

         

      • 對於結構體整體對齊:

      •              

 三、#pragma pack()自定義資料對齊規則

  • #pragma pack(n):每個特定平臺上的編譯器都有自己預設的對齊係數,程式設計師可以通過預編譯指令#pragma pack(n),n=1,2,4,8,16來改變這一系數
    • 資料成員對齊規則:結構或聯合的資料成員,第一個資料成員在offset為0的地方,以後每個資料成員的對齊方式都按照#pragma pack指定的數值這個資料成員自身佔用記憶體中比較小的那個進行。
    • 結構或聯合整體對其原則:在資料成員完成自身對齊後,結構或聯合本身也要進行對齊,對齊按照#pragma pack(n)指定的數值和結構或聯合最大資料成員佔用記憶體中比較小的那個進行。