C++: 内存对齐
什么是內存對齊?
所謂的內存對齊,就是為了讓內存存取更加有效率而采取的一種優化手段,對齊的結果是使得內存中數據的首地址是CPU單次獲取數據大小的整數倍。
比如,CPU單次獲取數據的大小是4個字節,對于 int x 而言,如果 x 的地址是0x00000000、0x00000004…等4的倍數,就是內存對齊。
此外,這里說的內存對齊,一般就是針對結構體來進行探討的,所以這就可以理解在本文后面提到的對整體和成員有不同的對齊方式了。
為什么要內存對齊?
1.硬件因素
經過內存對齊之后,CPU對內存訪問的效率會大大提高。
舉個例子:
● 對于int變量 x 占用4個字節的內存大小,假設它存放在 $ 0x00000003\sim0x00000006$ 的位置上,此時 0x00000003 不是4的整數倍。因此,對于每次只取4個字節的CPU而言,對 x 的讀取就必須分兩次進行,第一次讀取 $ 0x00000000\sim0x00000003$,第二次讀取 $ 0x00000004\sim0x00000007$,然后再進行拼接處理,才能得到我們想要的數據,可見這樣的效率會很低下。
● 倘若經過對齊,即數據的首地址是CPU單次獲取數據大小的整數倍,假設 x 存放在 $ 0x00000004\sim0x00000007$ 的位置,那么CPU只需要訪問一次內存就可以讀取出 x 的值了。
2.可移植性
不是所有的硬件平臺都能訪問任意地址上的任意數據的,例如有些平臺上CPU在內存非對齊的情況下執行二進制代碼會崩潰。為了代碼的可移植性,進行內存對齊是很有必要的。
如何進行內存對齊?
對齊方式
方式一: 編譯器提供了一種手動指定對齊值的方式,只要在代碼前添加關鍵字 #pragma pack(n) 即可,其中 n 是手動指定的內存對齊的字節數。比如 #pragma pack(4)表示以4個字節進行對齊。
方式二: 倘若沒有手動設置對齊值,或者手動設置的對齊值 n 大于成員變量中最大的類型的字節數(注意這一點!),編譯器則會默認將成員變量中最大的類型的字節數設置為對齊值(假設為 m)。
對齊規則
● 成員對齊: ① 第一個成員的首地址為0
? ② 假設某成員的類型所占字節數為 k,則該成員的首地址為 min(n,k) 的整數倍。
● 整體對齊: 結構體總的大小,應該為 min(n,m) 的整數倍,如果不夠就在后面填補占位。
補充:如果不能理解上面說的 min(n,k)和 min(n,m),可以看下面的解釋:
● 對于min(n,k) 的理解:若手動設置了對齊值 n,且 n<=k,那么首地址就是 n 的倍數,也就是上面的對齊方式一;若 k<n,根據對齊方式二可知,編譯器不會將 n 作為對齊值,而是會選擇成員中最大類型的字節數(即 m)作為對齊值,由于 m>=k,則該值必然也是 k 的整數倍,因此 min(n,k) 就可以理解啦。
● 對于min(n,m) 的理解:根據對齊方式一和二,其實系統的對齊值就是n和m中最小的那個。當然,整體對齊的意思是整個結構體的總大小要對齊,不夠就填補占位。比如,假設對齊值為8,結構體各個成員對齊之后的大小為12,由于12不是8的整數倍,所以編譯器會繼續填補4個空位,最終結構體的總大小為16。
代碼解釋
對于方式一,手動設置對齊值 #pragma pack(n),且n不大于成員變量的最大類型,此時編譯器的對齊值就是 n。
#include
#pragma pack(4) //對齊值為4using namespace std;struct MyStruct
{
char c;
double b;
int a;
};
int main() {
MyStruct data;
cout << sizeof(data.a) << endl; //結果為4
cout << sizeof(data.b) << endl; //結果為8
cout << sizeof(data.c) << endl; //結果為1,自動填充3個字節
cout << sizeof(data) << endl; //結果為16,如果對齊值設置為8,這里結果就是24//system(“pause”);return 0;
}
對于方式二,先看不進行手動設置對齊值的情況,編譯器默認將成員中最大類型的字節數作為對齊值,即double的類型大小,為 8,具體看代碼:
對于方式二,手動設置對齊值n,且n大于成員變量中的最大類型的字節數m,則編譯器采用m作為對齊值。
#include<iostream> #pragma pack(16) //設置對齊值為16,實際對齊值為sizeof(double)=8using namespace std;struct MyStruct { int a; double b; char c; };int main() { MyStruct data; cout << sizeof(data.a) << endl; //結果為4,自動填充4個字節 cout << sizeof(data.b) << endl; //結果為8 cout << sizeof(data.c) << endl; //結果為1,自動填充7個字節 cout << sizeof(data) << endl; //結果為24//system("pause");return 0; }總結
用兩句話來總結一下內存的對齊方式:
① 若沒有手動設置對齊值,則編譯器默認使用成員變量中最大的類型的字節數作為對齊值;
② 若手動設置了對齊值,則編譯器會在默認對齊值和手動設置的對齊值之間選擇最小的那個作為最終對齊值。
總結
- 上一篇: 单机 amp; 弱联网手游 防破解、金币
- 下一篇: lol 细节知识