記憶體位址對齊

2022-02-08 05:09:27 字數 3939 閱讀 4300

記憶體位址對齊,是一種在計算機記憶體中排列資料、訪問資料的一種方式,包含了兩種相互獨立又相互關聯的部分:基本資料對齊和結構體資料對齊。當今的計算機在計算機記憶體中讀寫資料時都是按字(word)大小塊來進行操作的(在32位系統中,資料匯流排寬度為32,每次能讀取4位元組,位址匯流排寬度為32,因此最大的定址空間為2^32=4gb,但是最低2位a[0],a[1]是不用於定址,a[2-31]才能儲存器相連,因此只能訪問4的倍數字址空間,但是總的定址空間還是2^30*字長=4gb,因此在記憶體中所有存放的基本型別資料的首位址的最低兩位都是0,除結構體中的成員變數)。基本型別資料對齊就是資料在記憶體中的偏移位址必須等於乙個字的倍數,按這種儲存資料的方式,可以提公升系統在讀取資料時的效能。為了對齊資料,可能必須在上乙個資料結束和下乙個資料開始的地方插入一些沒有用處位元組,這就是結構體資料對齊。

舉個例子,假設計算機的字大小為4個位元組,因此變數在記憶體中的首位址都是滿足4位址對齊,cpu只能對4的倍數的位址進行讀取,而每次能讀取4個位元組大小的資料。假設有乙個整型的資料a的首位址不是4的倍數(如下圖所示),不妨設為0x00fffff3,則該整型資料儲存在位址範圍為0x00fffff3~0x00fffff6的儲存空間中,而cpu每次只能對4的倍數記憶體位址進行讀取,因此想讀取a的資料,cpu要分別在0x00fffff0和0x00fffff4進行兩次記憶體讀取,而且還要對兩次讀取的資料進行處理才能得到a的資料,而乙個程式的瓶頸往往不是cpu的速度,而是取決於記憶體的頻寬,因為cpu得處理速度要遠大於從記憶體中讀取資料的速度,因此減少對記憶體空間的訪問是提高程式效能的關鍵。從上例可以看出,採取記憶體位址對齊策略是提高程式效能的關鍵。

舉例:首先我們先看看下面的c語言的結構體:

typedef struct

memalign

memalign;

以上這個結構體占用記憶體多少空間呢?也許你會說,這個簡單,計算每個型別的大小,將它們相加就行了,以32為平台為例,int型別佔4位元組,char占用1位元組,所以:4 + 3 + 4 = 11,那麼這個結構體一共占用11位元組空間。好吧,那麼我們就用實踐來證明是否正確,我們用sizeof運算子來求出這個結構體占用記憶體空間大小,sizeof(memalign),出乎意料的是,結果居然為12?看來我們錯了?當然不是,而是這個結構體被優化了,這個優化有個另外乙個名字叫「對齊」,那麼這個對齊到底做了什麼樣的優化呢,聽我慢慢解釋,再解釋之前我們先看乙個圖,圖如下:

相信學過彙編的朋友都很熟悉這張圖,這張圖就是cpu與記憶體如何進行資料交換的模型,其中,左邊藍色的方框是cpu,右邊綠色的方框是記憶體,記憶體上面的0~3是記憶體位址。這裡我們這張圖是以32位cpu作為代表,我們都知道,32位cpu是以雙字(dword)為單位進行資料傳輸的,也正因為這點,造成了另外乙個問題,那麼這個問題是什麼呢?這個問題就是,既然32位cpu以雙字進行資料傳輸,那麼,如果我們的資料只有8位或16位資料的時候,是不是cpu就按照我們資料的位數來進行資料傳輸呢?其答案是否定的,如果這樣會使得cpu硬體變的更複雜,所以32位cpu傳輸資料無論是8位或16位都是以雙字進行資料傳輸。那麼也罷,8位或16位一樣可以傳輸,但是,事情並非像我們想象的那麼簡單,比如,乙個int型別4位元組的資料如果放在上圖記憶體位址1開始的位置,那麼這個資料占用的記憶體位址為1~4,那麼這個資料就被分為了2個部分,乙個部分在位址0~3中,另外一部分在位址4~7中,又由於32位cpu以雙字進行傳輸,所以,cpu會分2次進行讀取,一次先讀取位址0~3中內容,再一次讀取位址4~7中資料,最後cpu提取並組合出正確的int型別資料,捨棄掉無關資料。那麼反過來,如果我們把這個int型別4位元組的資料放在上圖從位址0開始的位置會怎樣呢?讀到這裡,也許你明白了,cpu只要進行一次讀取就可以得到這個int型別資料了。沒錯,就是這樣,這次cpu只用了乙個週期就得到了資料,由此可見,對記憶體資料的擺放是多麼重要啊,擺放正確位置可以減少cpu的使用資源。

那麼,記憶體對齊有哪些原則呢?我總結了一下大致分為三條:

第一條:第乙個成員的首位址為0

第二條補充:以4位元組對齊為例,如果自身大小大於4位元組,都以4位元組整數倍為基準對齊。

第三條:最後以結構總體對齊。

第三條補充:以4位元組對齊為例,取結構體中最大成員型別倍數,如果超過4位元組,都以4位元組整數倍為基準對齊。(其中這一條還有個名字叫:「補齊」,補齊的目的就是多個結構變數挨著擺放的時候也滿足對齊的要求。)

上述的三原則聽起來還是比較抽象,那麼接下來我們通過乙個例子來加深對記憶體對齊概念的理解,下面是乙個結構體,我們動手算出下面結構體一共占用多少記憶體?假設我們以32位平台並且以4位元組對齊方式:

#pragma pack(4)typedef 

struct

memalign

memalign;

下圖為對齊後結構如下:

我們就以這個圖來講解是如何對齊的:

第乙個成員(char a[18]):首先,假設我們把它放到記憶體開始位址為0的位置,由於第乙個成員佔18個位元組,所以第乙個成員占用記憶體位址範圍為0~18。

第二個成員(double b):由於double型別佔8位元組,又因為8位元組大於4位元組,所以就以4位元組對齊為基準。由於第乙個成員結束位址為18,那麼位址18並不是4的整數倍,我們需要再加2個位元組,也就是從位址20開始擺放第二個成員。

第三個成員(char c):由於char型別佔1位元組,任意位址是1位元組的整數倍,所以我們就直接將其擺放到緊接第二個成員之後即可。

第四個成員(int d):由於int型別佔4位元組,但是位址29並不是4的整數倍,所以我們需要再加3個位元組,也就是從位址32開始擺放這個成員。

第五個成員(short e):由於short型別佔2位元組,位址36正好是2的整數倍,這樣我們就可以直接擺放,無需填充位元組,緊跟其後即可。

這樣我們記憶體對齊就完成了。但是離成功還差那麼一步,那是什麼呢?對,是對整個結構體補齊,接下來我們就補齊整個結構體。那麼,先讓我們回顧一下補齊的原則:「以4位元組對齊為例,取結構體中最大成員型別倍數,如果超過4位元組,都以4位元組整數倍為基準對齊。」在這個結構體中最大型別為double型別(佔8位元組),又由於8位元組大於4字 節,所以我們還是以4位元組補齊為基準,整個結構體結束位址為38,而位址38並不是4的整數倍,所以我們還需要加額外2個位元組來填充結構體,如下圖紅色的就是補齊出來的空間:

到此為止,我們記憶體對齊與補齊就完畢了!接下來我們用實驗來證明真理,程式如下:

#include #include 

//由於vs2010預設是8位元組對齊,我們

//通過預編譯來通知編譯器我們以4位元組對齊

#pragma pack(4) //

用於測試的結構體

typedef struct

memalign

memalign;

intmain()

程式執行過程中,檢視記憶體如下:

其中,各種顏色帶下劃線的代表各個成員變數,藍色方框的代表為記憶體對齊時候填補的多餘位元組,由於這裡看不到補齊效果,我們接下來看下圖,下圖籃框包圍的位元組就是與上圖的交集以外的部分就是補齊所填充的位元組。

在最後,我在談一談關於補齊的作用,補齊其實就是為了讓這個結構體定義的陣列變數時候,陣列內部,也同樣滿足記憶體對齊的要求,為了更好的理解這點,我做了乙個跟本例子相對照的圖:

記憶體位址的對齊

3 有 pragma pack int 進行設定,如果結構體某成員的sizeof大於你設定的,則按你的設定來對齊 注意 每次用 pragma pack int 進行設定後,要用 pragma pack 對其結束,免得造成錯誤 view plain copy to clipboard print in...

自然對齊(記憶體位址)

c 中 基本資料型別的變數不能簡單的儲存於記憶體中的任意位址,他們的起始位址必須能被他們的大小整除。typedef unsigned char byte enum color struct eigth double m price color m color bool m isshift bool ...

記憶體位址對齊方式

對齊的作用和原因 各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台的要求對資料存放進行對齊,會在訪問效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果乙個int型 假設為 32位 如...