C++ 有些細節雖然不知道可能不會影響你的程式碼正確執行, 不過卻可能影響你的程式耗用的記憶體。舉例來說, 底下這個程式定義了兩個幾乎相同的結構, 只是成員的順序不一樣, 不過如果顯示這兩個結構的大小, 就會發現 data2 要比 data1 多耗費了 4 個位元組:
#include <iostream>
using namespace std;
struct data1 {
char c1;
char c2;
int i1;
int i2;
};
struct data2 {
char c1;
int i1;
char c2;
int i2;
};
int main() {
cout << "sizeof(data1) = " << sizeof(data1) << "\n";
cout << "sizeof(data2) = " << sizeof(data2) << "\n";
return 0;
}
實際執行結果如下:
sizeof(data1) = 12
sizeof(data2) = 16
要理解為什麼, 就必須理解 C++ 中個別型態的位址對齊 (alignment)。
位址對齊 (alignment)
在 C++ 中, 為了存取記憶體效率的關係, 不同型態的物件必須放在特定數值倍數的位址上, 這就稱為位址對齊。你可以透過 alignof
運算子得知指定型態物件的位置對齊數值:
#include <iostream>
using namespace std;
int main()
{
cout << "alignof(int): " << alignof(int) << endl; // 4
cout << "alignof(double): " << alignof(double) << endl; // 8
cout << "alignof(char): " << alignof(char) << endl; // 1
cout << "alignof(float): " << alignof(float) << endl; // 4
return 0;
}
輸出結果如下:
alignof(int): 4
alignof(double): 8
alignof(char): 1
alignof(float): 4
以 int
為例, 如果現在可用的記憶體位址從 1017 開始, 若要配置一個 int
變數, 因為它的位址對齊數值是 4, 就必須往後到 4 的倍數, 也就是位址 1020 開始才能配置。反觀 char
, 它的位址對齊數值是 1, 所以如果是要配置 char
變數, 就可以直接從 1017 配置, 不需要移動。
位址對齊照成的補空 (padding)
剛剛看到的 int
變數例子, 就會浪費 1017 到 1019 之間的 3 個位元組, 這稱為補空 (padding), 也就是照成文章一開始兩個成員相同但順序不同的結構大小不同的原因。
以 data1
為例:
struct data1 {
char c1;
char c2; // 這裡需要補空 2 個位元組
int i1;
int i2;
};
因為 char
的位址對齊數值為 1, 所以前兩個 char
成員可以相鄰配置, 但是 int
的位址對齊數值為 4, 所以在這兩個 char
成員之後要補空 2 個位元組, 變成佔用 4 個位元組, 加上後面兩個 int
個別佔 4 個位元組, 所以共佔用 12 個位元組。
以下則是 data2
:
struct data2 {
char c1; // 這裡需要補空 3 個位元組
int i1;
char c2; // 這裡需要補空 3 個位元組
int i2;
};
因為 char
與 int
成員交錯, 所以每個 char
成員之後需要補空 3 個位元組, 才能讓 int
成員位址對齊 4 的倍數, 導致兩個 char
成員都佔用了 4 個位元組, 加上兩個 int
成員, 所以總共佔用了 16 個位元組。
瞭解這個差異後, 你就可以在必要的時候, 調整成員的順序, 減少記憶體的用量了。
Top comments (0)