資料對齊,是指資料所在的記憶體位址必須是該資料長度的整數倍。cpu的優化規則大致原則是這樣的:對於n位元組的元素(n=2,4,8,...),它的首位址能被n整除,才能獲得最好的效能。設計編譯器的時候,可以遵循這個原則:對於每乙個變數,可以從當前位置向後找到第乙個滿足這個條件的位址作為首位址。故而dword資料的記憶體起始位址能被4除盡,word資料的記憶體開始位址能被2整除。x86cpu能直接訪問對齊的資料,當它試圖訪問乙個未對齊的資料時,會在內部進行一系列的調整,這些調整雖然對程式不可見,但是會影響程式的執行速度。
對於結構體資料,預設情況下,為了方便對結構體元素的訪問和管理,當結構體內的元素都小於處理器長度的時候,便以結構體裡面最長的資料為對齊單位,也就是說,結構體的長度一定是最長資料長度的整數倍。如果結構體內部存在長度大於處理器位數時就以處理器位數為對齊單位。對於類,如果只有資料部分而沒有成員函式,那麼也要進行記憶體對齊
一、記憶體對齊的原因
大部分的參考資料都是如是說的:
1、平台原因(移植原因):不是所有的硬體平台都能訪問任意位址上的任意資料的;某些硬體平台只能在某些位址處取某些特定型別的資料,否則丟擲硬體異常。
2、效能原因:資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問;而對齊的記憶體訪問僅需要一次訪問。
二、對齊規則
每個特定平台上的編譯器都有自己的預設「對齊係數」(也叫對齊模數)。程式設計師可以通過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一係數,其中的n就是你要指定的「對齊係數」。
規則:1、資料成員對齊規則:結構(struct)(或聯合(union))的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員的對齊按照#pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在資料成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行。
3、結合1、2顆推斷:當#pragma pack的n值等於或超過所有資料成員長度的時候,這個n值的大小將不產生任何效果
三、試驗
我們通過一系列例子的詳細說明來證明這個規則吧!
我試驗用的編譯器包括gcc 3.4.2和vc6.0的c編譯器,平台為windows xp + sp2。
我們將用典型的struct對齊來說明。首先我們定義乙個struct:
#pragma pack(n) /* n = 1, 2, 4, 8, 16 */
struct test_t ;
#pragma pack(n)
首先我們首先確認在試驗平台上的各個型別的size,經驗證兩個編譯器的輸出均為:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
我們的試驗過程如下:通過#pragma pack(n)改變「對齊係數」,然後察看sizeof(struct test_t)的值。
1、1位元組對齊(#pragma pack(1))
輸出結果:sizeof(struct test_t) = 8 [兩個編譯器輸出一致]
分析過程:
1) 成員資料對齊
#pragma pack(1)
struct test_t ;
#pragma pack()
成員總大小=8
2) 整體對齊
整體對齊係數 = min((max(int,short,char), 1) = 1
整體大小(size)=$(成員總大小) 按 $(整體對齊係數) 圓整 = 8 /* 8%1=0 */ [注1]
2、2位元組對齊(#pragma pack(2))
輸出結果:sizeof(struct test_t) = 10 [兩個編譯器輸出一致]
分析過程:
1) 成員資料對齊
#pragma pack(2)
struct test_t ;
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊係數 = min((max(int,short,char), 2) = 2
整體大小(size)=$(成員總大小) 按 $(整體對齊係數) 圓整 = 10 /* 10%2=0 */
3、4位元組對齊(#pragma pack(4))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員資料對齊
#pragma pack(4)
struct test_t ;
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊係數 = min((max(int,short,char), 4) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊係數) 圓整 = 12 /* 12%4=0 */
4、8位元組對齊(#pragma pack(8))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員資料對齊
#pragma pack(8)
struct test_t ;
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊係數 = min((max(int,short,char), 8) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊係數) 圓整 = 12 /* 12%4=0 */
5、16位元組對齊(#pragma pack(16))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員資料對齊
#pragma pack(16)
struct test_t ;
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊係數 = min((max(int,short,char), 16) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊係數) 圓整 = 12 /* 12%4=0 */
記憶體中的資料對齊
為何要位元組對齊 簡單來說就是提高cpu對記憶體的訪問效率。為了訪問未對齊的記憶體,處理器需要作兩次記憶體訪問 然而,對齊的記憶體訪問僅需要一次訪問。比如有些平台每次讀都是從偶位址開始,如果乙個int型 假設為32位系統 存放在偶位址開始的地方 那麼讀乙個週期就可以讀出這32bit,而如果存放在奇位...
C中記憶體對齊問題
1 對於基本資料型別 許多計算機系統對基本資料型別可允許位址作了一定的限制,要求某種型別物件的位址必須是某個值n 通常是2 4 8 的倍數,從而來簡化處理器和儲存器之間的介面的硬體設計。如linux的對齊策略是2位元組資料型別,例如short的位址必須是2的倍數。而較大的資料型別如 int int ...
C C 中記憶體對齊問題
我們通過幾個例子來全面搞懂c c 中的記憶體對齊問題。來看看下面的結構體大小分別是多大?假設均在32位機器上 struct a 這個相信大家都知道,由於變數都是char型別,對齊值為1,所以結構體大小為3。struct b 對齊值為4,結構體大小為 4 4 8。第乙個4為int,第二個4裡面包含乙個...