C++中什麼是記憶體對齊?
以下資料是我從別人的文章抽取出來的,我認為比較有利於理解。加上一點我的理解
接下來我們好好討論一下記憶體對齊的作用
1.平臺原因(移植原因):不是所有的硬體平臺都能訪問任意地址上的任意資料,某些硬體平臺只能在某些地址處取某些特定型別的資料,否則丟擲硬體異常
2.硬體原因:經過記憶體對齊之後,CPU的記憶體訪問速度大大提升。具體原因接下來解釋
圖一:
我們普通程式設計師心中的記憶體印象,由一個個位元組組成,但是CPU卻不是這麼看待的
圖二:
cpu把記憶體當成是一塊一塊的,塊的大小可以是2,4,8,16 個位元組,因此CPU在讀取記憶體的時候是一塊一塊進行讀取的,塊的大小稱為(memory granularity)記憶體讀取粒度。
我們再來看看為什麼記憶體不對齊會影響讀取速度?
假設CPU要讀取一個4位元組大小的資料到暫存器中(假設記憶體讀取粒度是4),分兩種情況討論:
1.資料從0位元組開始
2.資料從1位元組開始
解析:當資料從0位元組開始的時候,直接將0-3四個位元組完全讀取到暫存器,結算完成了。
當資料從1位元組開始的時候,問題很複雜,首先先將前4個位元組讀到暫存器,並再次讀取4-7位元組的資料進暫存器,接著把0位元組,4,6,7位元組的資料剔除,最後合併1,2,3,4位元組的資料進暫存器,對一個記憶體未對齊的暫存器進行了這麼多額外操作,大大降低了CPU的效能。
但是這還屬於樂觀情況,上文提到記憶體對齊的作用之一是平臺的移植原因,因為只有部分CPU肯幹,其他部分CPU遇到未對齊邊界就直接罷工了。
參考圖片:
上面的兩幅圖已經說得很清楚了,如果不記憶體對齊會有什麼後果。假如一個int型別的數,它一開始就存在0~3號記憶體中,由於我們的cpu是整塊整塊地進行資料讀取,那麼cpu就能直接將這塊資料取出來。
那如果在1~4號呢,那麼cpu就得先把兩塊記憶體取出來,將0~3號記憶體的1~3位取出來,然後再將4號的那一位取出來,存入暫存器中。這樣子就影響了效率。那麼我們乾脆浪費空間,也要讓cpu能夠一次就取出來。這就是記憶體對齊。
接下來說說記憶體對齊的計算方法。
#include<iostream> using namespace std; struct A{ char a; int b; short c; }; struct B{ short c; char a; int b; }; int main(){ A x; B y; cout<<endl; int *u=(int *)&x.a; int *t=(int *)&y.a; x.a='a';x.b=1;x.c=1; y.a='a';y.b=1;y.c=1; cout<<u<<endl; cout<<&x.b<<endl; cout<<&x.c<<endl; cout<<&y.c<<endl; cout<<t<<endl; cout<<&y.b<<endl; cout<<"sizeof(A): " <<sizeof(A)<<endl; cout<<"sizeof(B): " <<sizeof(B)<<endl; return 0; }
以下是輸出結果
我們以A為例子分析:
我在windows和Linux平臺測試了,兩個平臺的#pragma pack()都應該是4。
對結構體的各成員來說,第一個成員位於偏移量為0的位置,之後的資料成員偏移量必須是 min(#pragma pack(),該資料成員自身長度) 的倍數。因為char為第一個成員,所以其偏移量為0,int為第二個成員,其偏移量為min(4,4)=4;在下面的圖中,地址為4的倍數就是04了,所以int從這裡開始。接著是short,偏移量為min(4,2)=2;偏移量為2的倍數的就是8,所以從08開始。由於結構沒有成員了,那麼也會湊夠一個#pragma pack()。即一直到11這個位置。
假如你在short 後面加一個或者兩個char,長度也仍然為12
以上內容來自 :https://www.cnblogs.com/jijiji/p/4854581.html