內存對齊是 C/C++ 開發者必須攻克的硬核知識點,它直接決定結構體大小、訪問效率、跨平台數據傳輸正確性。本文從硬件動機、編譯器算法、#pragma pack 控制、聯合體對齊、經典面試題五個維度建立完整知識體系。
四個關鍵值:
char=1, short=2, int/float/pointer=4, double=8 (64位下 pointer=8)。#pragma pack(n) 設定的 n (1/2/4/8/16)。min(自身對齊值, 指定對齊值) —— 決定成員放置位置的最終值。offset % 有效對齊值 == 0 的位置。sizeof % 結構體有效對齊值 == 0 的值(尾部填充)。// 默認 pack(4) 環境
struct A { int a; char b; short c; }; // sizeof = 8
// 佈局: a[0-3], b[4], padding[5], c[6-7]
struct B { char b; int a; short c; }; // sizeof = 12
// 佈局: b[0], padding[1-3], a[4-7], c[8-9], padding[10-11]
#pragma pack(2)
struct C { char b; int a; short c; }; // sizeof = 8
// 有效對齊: b=1, a=min(4,2)=2, c=2
// 佈局: b[0], a[2-5], c[6-7] → 總 8,滿足結構體有效對齊 2
#pragma pack(1)
struct D { char b; int a; short c; }; // sizeof = 7 (緊湊)
// 推薦寫法:成對使用,避免污染全局
#pragma pack(push, 1) // 保存舊值,設為 1 字節對齊
struct NetPacket {
uint32_t seq;
uint16_t len;
uint8_t payload[0]; // 柔性數組
};
#pragma pack(pop) // 恢復舊值
// 網絡協議、文件格式、共享內存必須顯式指定 pack
// 不同平台編譯同一結構體時,務必保證 pack 值一致
union U {
int a[5]; // 20 字節
char b; // 1
double c; // 8,自身對齊 8
}; // sizeof = 24 (非 20!)
// 原因:聯合體對齊 = 成員最大對齊 = 8
// 總大小 = max(成員尺寸) 向上取整到 8 的倍數 = 24
struct S {
int n; // 4
union U u; // 24,需 8 對齊 → 前填 4 字節
char c[10]; // 10,尾部填 6 字節對齊 8
}; // sizeof = 4 + 4(pad) + 24 + 10 + 6(pad) = 48
#pragma pack(8)
struct Ex1 { short a; long b; }; // long=4, max align=4 → 有效=4, sizeof=8
struct Ex2 { char c; Ex1 e1; short d; };
// Ex2 成員: c(1), e1(8, align=4), d(2)
// 佈局: c[0], pad[1-3], e1[4-11], d[12-13], pad[14-15] → sizeof=16
// &e1 - &Ex2 = 4
#pragma pack()
int main() {
Ex2 e2;
printf("%zu\n%zu\n%zu\n", sizeof(Ex1), sizeof(Ex2), (size_t)(&e2.e1) - (size_t)(&e2));
}
// 輸出: 8 16 4
#pragma pack(1) + 手動處理大端序(htonl/ntohl)。double),除非必要。static_assert(sizeof(Struct) == EXPECTED, "layout changed"); (C11)。內存對齊本質是「空間換時間」的工程妥協。透徹理解編譯器佈局算法,能讓你精準控制數據結構大小、排查跨平台通訊異常、在面試中展現系統級思維深度。