C 深陷 之「記憶體對齊」

2021-10-07 02:12:49 字數 2243 閱讀 6067

現代計算機在處理資料時,按照某個「單位」來處理。32位機器,每次處理32位、4位元組的二進位制資料,64位同理。

記憶體對齊指的是計算機系統對基本資料型別合法位址做出了一些限制,要求某種型別物件的位址必須是某個值的倍數。

本文著重於記憶體對齊的基本原理,有關複雜情況下(虛函式、繼承等)的記憶體對齊方案,請參考【c++深陷】之「類記憶體布局」。

舉例來說:

class

myclass

;

若記憶體未對齊,如下圖:

上圖左側數字表示記憶體位址,上方綠色數字表示相對於品紅色數字的偏移位元組;乙個紫色的小格仔表示乙個位元組。圖示為32bit機器的儲存結構。

每次處理my_class.d時候,都要讀出854620位置的資料,裁減後段;讀出854624位置的資料,全部需要;讀出854628位置的資料,裁減出前段,然後拼接得到my_class.d的值,無疑浪費了很多時間。

對齊後,如下圖:

每次處理my_class.d就簡單多了,雖然浪費了空間,帶來的卻是效率的提公升。

記憶體對齊有三個原則:

確定對齊單位k,k的單位是位元組,取下面三個數值的最小值:

32位機器預設4位元組,64位機器預設8位元組;

預編譯指令#pragma pack(n)中的n的值,n取1、2、4、8、16;

結構體或類中長度最大成員;

除了第乙個成員資料外,其餘各成員資料距首位址偏移量為min(k, p)的整數倍,其中p為當前成員資料的位元組數;

結構體或類的整體大小,必須為k的整數倍。

舉例myclass來說(假設int佔4位元組,char佔1位元組,double佔8位元組):

class

myclass

;

我的電腦是64位機器,取8;預編譯指令沒有設定過,我的電腦是8;myclass中最大的資料成員double型別,也是8;取三者最小,因此對齊單位:k = 8;

①第乙個成員int i在首位,不需要偏移,佔4個位元組;②第二個成員char c佔1個位元組,放在min(8, 1) = 1的整數倍位置,因此可以直接放在int後面,偏移為4;③第三個成員double d不可以直接放在c的後面,這樣它的偏移是5。d必須放在min(8, 8) = 8的整數倍的位置,可以取8,所以空出來3個位元組。

排滿之後,類的整體為16個位元組,4(int) + 1(char) + 3(浪費) + 8(double) = 16,符合8的整數倍。

因此記憶體布局如下圖:

再舉一例:

class

myclass

;

前三個成員都一樣的,主要是最後乙個成員,要遵循第三個原則。

當把char p新增到原來的double d後面時,可以直接緊接著double d,偏移為16;這樣算出來的類的大小為16 + 1(char) = 17,不符合第三條原則,因此需要填充到24(8的3倍),即新增7個浪費的位元組,4(int) + 1(char) + 3(浪費) + 8(double) + 1(char) + 7(浪費) = 24。

最終結果如下圖:

c++中的記憶體對齊,通過犧牲空間的方式,換來了執行效率的提公升。它通過三個原則,按照類或結構體中的宣告順序,逐步確認每個成員的位置,最終實現了成員資料的記憶體對齊。

本文只討論了最簡單的對齊情況,對於複雜的結構體和類,要考慮靜態成員、虛函式、繼承等情況,詳細內容請參考【c++深陷】之「類記憶體布局」。

編譯之 記憶體對齊

現代計算機中記憶體空間都是按照 byte 劃分的,也就是位元組,從理論上講似乎對任何型別的變數的訪問可以從任何位址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體位址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的乙個接乙個的排放,這就是對齊 從cpu的角度來說,cpu...

C 我的理解之記憶體對齊

第二條規則 vs下用 progma pack n 用來設定記憶體對齊的係數,每次將要對齊的資料成員長度和n來比較,小的那個乙個作為標準來對齊。第三條規則 將所有資料成員對齊之後,整個class或者struct也要進行對齊,它的大小必須是這個類中所佔位元組最大的資料成員的位元組的整數倍數 接下來我將使...

C語言結構體之記憶體對齊

首先看乙個例子,下面有乙個結構體 struct structtest1 假設這個結構體成員在記憶體中是緊湊排列的,那麼c1的儲存位址就是0,s的儲存位址是1 2,c2的儲存位址是3,i的儲存位址是4 7,c1的位址是0000000000000000,s的位址是0000000000000001,c2的...