大話C++之:記憶體對齊
在使用sizeof()計算結構體和class大小的時候,往往很多時候計算出來並不是我們想要的結果。最近在使用的時候就特別想了解它內在的原理,搞懂它的本質。
看一下下面這個例子:
struct
Struct
{
char
a
;
int
i
;
};
問題是:這個結構體大小是多少?
很多人回答可能是5個位元組。分析結構體的組成是:char佔1個字,int佔4個位元組。1+4=5,沒錯吧?
其實這個答案應該是:不確定。因為它給的條件不能夠得出一個確定的答案。
開啟VS 2017,64位程式執行一下,得出的結果是8。
既然它不是簡單的把成員大小簡單累加,想必它肯定是有一套規則,那是什麼規則呢?那就是記憶體對齊
(what)什麼記憶體對齊(Data alignment)?
百度百科的定義:編譯器為程式中的每個“資料單元”安排在適當的位置上。
我的理解是按照一定的規則把它的位置排好,排列得有規有矩,方便計算機讀取。
(why)為什麼要使用記憶體對齊
為了效能。為什麼記憶體對齊為提高效能呢?舉個例子,例如把4個位元組從地址1寫入到暫存器中。首先從前4個位元組中讀取3個節字,再從後面4個位元組中讀了1個字,最後放在暫存器中合併在一齊。讀取一個數據到暫存器中消耗了這麼多步驟,對於CPU效能來說是挺大的負擔。如果是位元組對齊了,從0開始連續讀取4個字,暫存器剛好也是4個位元組,只需要一次讀寫即可,大大提高了讀取效率。
3。 (how)記憶體對齊是如何工作的
想想我們平時要把物品整理對齊的時候,一般都有參照物或對齊的標準。如果把箱子排好,是一個個緊密對齊排列呢,還是間隔排列呢,這個標準會影響排列後使用的空間。
對於記憶體對齊,同樣也是有一個參考標準。編譯器中提供了#pragma pack(n)來設定變數以n位元組對齊方式,n即為對齊的標準。
VS 2017 64bit的位元組對齊變數為16,在程式碼中插入:#pragma pack(show),就可以檢視當前環境的變數值。
有了這個標準,還需要的是被排列的物件:欄位型別。
拿回箱子排列的例子來說,可能每一個箱子的大小不一樣,有些是短一點,有些是長一點,可能排列的位置也有不一樣。
箱子大小就如成員變數,每一個變數有自己的大小。
現在來看一步步分解,系統是如何把位置給安排得明明白白的。
例如這個
struct
Struct
{
char
a
;
int
i
;
};
每一塊記憶體記憶體是從0開始偏移的,第一個欄位直接安排下去。
第一個欄位char,偏移量(offset)為0,佔用空間1個位元組,結束位置為1;
關鍵一步來了,如何安排int位元組呢?
int的大小是4 bytes,位元組對齊變數為16 bytes。比較兩者間選擇小的一個,此時4 bytes作為偏移參考量X。
從當前偏移量1開始,尋找偏移參考量X最小的倍數位置。4的倍數可以是:4,8,12,16等等,當前最小的為4。然而1,2,3的位置空了,需要填充,從第4個byte開始連續4個byte為int的空間。
此時8個位元組的排布為:a——-iiii;
畫重點:char型佔用空間為1 byte,則其起始位置必須可被1整除。int為4 bytes,其起始位置必須是可以被4整除。
記憶體對齊的過程如上,影響同一個結構體的記憶體佈局的關鍵是:以多少位元組來對齊,如上面所說的n。最開始的例子,實際上可以透過設定n為1,可以得到size為5。
回顧一下最開始的問題,缺少一個指定變數n的條件,故不能確定大小是多少。