在 C++ 開發中,字節對齊是一個底層卻至關重要的主題,它直接影響結構體的內存佈局、數據訪問效率,甚至決定跨平台移植的成敗。本文結合實戰經驗,深入剖析兩個極易被忽視的字節對齊陷阱:聯合體的對齊行為,以及不同編譯器環境下的默認對齊差異。
先看一段帶有 #pragma pack(4) 指令的代碼:
#pragma pack(4)
struct com {
union {
double dTest;
int nTest;
char szTest[14];
};
char chTest1;
char chTest2;
};
#pragma pack()
問題: sizeof(struct com) 的結果是多少?
在 GCC 4.1 和 VC 2005 環境下,答案均為 20 字節。
通過調試發現,聯合體內部自動增加了 2 字節填充,以滿足其自身 4 字節對齊要求。聯合體實際佈局如下:
union {
double dTest; // 8 字節,偏移 0
int nTest; // 4 字節,偏移 0
char szTest[14]; // 14 字節,偏移 0
byte Padding1[2]; // 編譯器自動插入的 2 字節填充
}; // 聯合體總大小:16 字節
接著,結構體繼續佈局:
#pragma pack(4)
struct com {
union { ... }; // 16 字節,偏移 0
char chTest1; // 1 字節,偏移 16
char chTest2; // 1 字節,偏移 17
byte Padding2[2]; // 結構體尾部填充,保證整體 4 字節對齊
};
#pragma pack()
關鍵結論: 聯合體的大小不僅取決於最大成員,還必須滿足「自身對齊數 = min(編譯器對齊數, 最大成員大小)」的規則,且會在尾部填充以達到自身對齊數的整數倍。
做平台移植的開發者務必注意:永遠不要假設默認對齊行為一致,必須實測驗證。以下是主流環境的差異總結:
| 平台 / 編譯器 | 默認對齊數 | 支持的對齊值 | 關鍵行為差異 |
|---|---|---|---|
| Win32 / VC | 8 字節 | 1, 2, 4, 8, 16 | 支持較大對齊,double/long long 可 8 字節對齊 |
| Linux 32-bit / GCC 4.1 | 4 字節 | 1, 2, 4 | 即使指定 #pragma pack(8),double/long long 仍按 4 字節對齊 |
| Android 4.0 / ARM NDK | 視架構而定 | 1, 2, 4, 8 | 與 PC Linux 32 不同:double/long long 按 8 字節對齊 |
#pragma pack(n) 或屬性 __attribute__((aligned(n))) 顯式聲明,避免依賴默認值。sizeof 和 offsetof,納入 CI 流程,確保各平台佈局一致。memcpy 結構體內存。字節對齊看似微不足道,卻是 C++ 程序穩定性與可移植性的基石。掌握這兩大易忽視點,能讓你在跨平台開發中少踩無數坑。