早年編寫 MBR 分區表解析代碼,邏輯無誤卻運行錯誤,最終發現是結構體內存對齊導致布局與磁盤實際不符。這一經歷揭示了理解內存對齊的重要性:它不僅關乎正確性,更直接影響性能。本文從硬件原理出發,系統闡述對齊規則、計算方法與實戰技巧。
CPU 以「字」為單位訪問內存。32 位機字長 4 字節,64 位為 8 字節。若變量跨字邊界(如 int 存放在偏移 1),CPU 需兩次讀取、移位、合並,效率大幅下降。
通過插入填充字節,使變量地址滿足對齊要求,換取單次訪問效率。典型權衡:浪費幾字節,換取訪問速度提升數倍。
| 類型 | 32位對齊 | 64位對齊 |
|---|---|---|
| char | 1 | 1 |
| short | 2 | 2 |
| int/float | 4 | 4 |
| double/long long/指針 | 4 | 8 |
#pragma pack(1) 取消填充,緊湊排列(網絡協議、文件格式常用)。#pragma pack() 恢復默認。struct A { char c; int d; short e; }; // 12 字節
struct B { char c; double d; int e; short f; }; // 24 字節 (64位)
推導 A:
c @0 (1字節) 填充 3 字節 @1-3 ← 為 int 對齊 d @4-7 (4字節) e @8-9 (2字節) 填充 2 字節 @10-11 ← 結構體對齊 4 的倍數 sizeof=12
struct Inner { char c; int d; short e; }; // 12
struct Outer {
char c; // @0
long long d; // @8 (8字節對齊,填充7)
int e; // @16
short f; // @20
Inner g; // @24 (Inner最大對齊4,24滿足)
char h; // @36
int i; // @40 (填充3)
}; // 總長 48 (8的倍數)
/Zp 控制 pack 值。#pragma pack(1) + 手動處理大小端。memcpy 結構體,逐字段編碼。offsetof 宏檢查成員偏移,sizeof 驗證總大小。alignas (C++11) 強制指定對齊:alignas(64) struct CacheLine { ... };成員按序排,自身對齊找; 不夠填補齊,結構體尾也要對齊; pack(n) 限上限,取小為準則; 數組連續放,首尾都對齊。
內存對齊是「硬件約束→編譯器實現→程序員感知」的傳導鏈。理解其本質是「CPU 訪問效率與內存空間的博弈」,掌握 sizeof/offsetof 實測技巧,善用 #pragma pack 與 alignas 控制手段,即可在正確性與性能間遊刃有餘。