C 記憶體對齊

2021-10-08 15:21:44 字數 2758 閱讀 6648

在c語言程式開發中,有時有經驗的程式設計師會提起「記憶體對齊」一詞,事實上,這也是c語言中結構體的 size 不等於它所有成員 size 之和的原因(c語言中的結構體的size,並不等於它所有成員size之和,為什麼?),那麼,c語言程式為什麼要「記憶體對齊」呢?

我們先看下面程式的輸出:

#include struct a;

int main()

在32位機器上char 佔1個位元組,int 佔4個位元組,short佔2個位元組,一共占用7個位元組.但是實際真的是這樣嗎?

測試輸出的結果是a: 12, 比計算的7多了5個位元組。這個就是因為編譯器在編譯的時候進行了記憶體對齊導致的。 

在32位系統中,1個記憶體單元的長度是8bits,佔乙個位元組。下圖展示了記憶體單元的實際面貌

cpu訪問記憶體時,並不是逐個位元組訪問,而是以字長(word size)為單位訪問。比如32位的cpu,字長為4位元組,那麼cpu訪問記憶體的單位也是4位元組。這麼設計的目的,是減少cpu訪問記憶體的次數,加大cpu訪問記憶體的吞吐量。

再比如同樣讀取8個位元組的資料,一次讀取4個位元組那麼就需要讀取2次。具體如下圖所示:

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

各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對資料存放進行對齊,會在訪問效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果乙個int型(假設為32位系統)如果存放在偶位址開始的地方,那麼乙個讀週期就可以讀出,而如果存放在奇位址開始的地方,就可能會需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該int資料。顯然在讀取效率上下降很多。這也是空間和時間的博弈。

我們假設cpu以4位元組為單位讀取記憶體。如果變數在記憶體中的布局按4位元組對齊,那麼讀取a變數只需要讀取一次記憶體,即word1;讀取b變數也只需要讀取一次記憶體,即word2。

而如果變數不做記憶體對齊,那麼讀取a變數也只需要讀取一次記憶體,即word1;但是讀取b變數時,由於b變數跨越了2個word,所以需要讀取兩次記憶體,分別讀取word1和word2的值,然後將word1偏移取後3個位元組,word2偏移取前1個位元組,最後將它們做或操作,拼接得到b變數的值。

顯然,記憶體對齊在某些情況下可以減少讀取記憶體的次數以及一些運算,效能更高。

另外,由於記憶體對齊保證了讀取b變數是單次操作,在多核環境下,原子性更容易保證。

記憶體對齊主要遵循下面三個原則:

結構體變數的起始位址能夠被其最寬的成員大小整除

結構體每個成員相對於起始位址的偏移能夠被其自身大小整除,如果不能則在前乙個成員後面補充位元組

結構體總體大小能夠被最寬的成員的大小整除,如不能則在後面補充位元組

其實這裡有點不嚴謹,編譯器在編譯的時候是可以指定對齊大小的,實際使用的有效對齊其實是取指定大小和自身大小的最小值,一般預設的對齊大小是4。

#pragma pack(n)

n 值 1 2 4 8 16......

預設linux pack(4) windows pack(8)

基本資料型別所佔記憶體大小

舉例說明這些規則

#pragma pack(4)

struct node;

struct node n;

printf("%d\n",sizeof(n));

n=12

pragma pack(4)

struct node;

struct node n;

printf("%d\n",sizeof(n));

n=12

//將對齊位數強制定位2

#pragma pack(2)

struct node;

struct node n;

printf("%d\n",sizeof(n));

n=10

#pragma pack(1)

struct node;

struct node n;

printf("%d\n",sizeof(n));

n=8

但是記憶體對齊提公升效能的同時,也需要付出相應的代價。由於變數與變數之間增加了填充,並沒有儲存真實有效的資料,所以占用的記憶體會更大。這也是乙個典型的空間換時間的應用場景。

參考:

C 記憶體對齊

vc6.0編譯器對記憶體對齊的管理方式遵循以下兩個原則 1.對於結構體內部變數的對齊方式 變數存放的起始位址相對於結構的起始位址的偏移量 char 偏移量必須為sizeof char 即1的倍數 int 偏移量必須為sizeof int 即4的倍數 float 偏移量必須為sizeof float ...

c 記憶體對齊

一.計算struct的size有兩個原則 pragma pack n n是編譯器的對齊位元組數 1 struct中各成員按照對齊原則 在為當前變數 設為a 分配記憶體時,要參考之前所有變數的偏移量之和 設為d d必須是min n,sizeof a 的倍數,否則編譯器會自動在最後補上缺少的位元組數。2...

C 記憶體對齊

c 中的記憶體對齊 記憶體對齊 在我們的程式中,資料結構還有變數等等都需要占有記憶體,在很多系統中,它都要求記憶體分配的時候要對齊,這樣做的好處就是可以提高訪問記憶體的速度。我們還是先來看一段簡單的程式 程式一 1 include 2 using namespace std 3 4structx1 ...