面試經典題:struct { char a; short b; char c; int d; },新手答 8,實際輸出 12/16。核心原因:**內存對齊**。
i 個成員偏移量為 min(sizeof(成員類型), 指定對齊數) 的整數倍。
struct Test {
char a; // offset 0, size 1
short b; // offset 2 (2字節對齊), size 2
char c; // offset 4, size 1
int d; // offset 8 (4字節對齊), size 4
}; // 總大小 12 (最大對齊 4 的倍數)
struct {
char a, b, c; // 連續 0,1,2
short d; // offset 4 (不跨越 4 字節邊界)
int e; // offset 8
}; // 總大小 12
char 佔 1 字節,放哪裡都單次訪問,故編譯器傾向緊湊排列。
編譯器指令,改變默認對齊參數(通常為 4/8)。
成員有效對齊 = min(成員自身大小, pack(n) 指定值)
#pragma pack(8)
struct Inner {
char a; // 對齊 1, offset 0
long b; // 對齊 min(4,8)=4, offset 4
}; // sizeof=8 (最大對齊 4 的倍數)
struct Outer {
char c; // 對齊 1, offset 0
struct Inner in; // 對齊 min(內層最大成員對齊=4, 8)=4, offset 4
long long d; // 對齊 min(8,8)=8, offset 16 (12→16 對齊8)
}; // sizeof=24
#pragma pack() // 恢復默認
#include
#pragma pack(8)
struct Inner { char a; long b; };
struct Outer { char c; struct Inner in; long long d; };
#pragma pack()
int main() {
struct Outer o;
printf("c:%p in.a:%p in.b:%p d:%p\n",
(void*)&o.c - (void*)&o,
(void*)&o.in.a - (void*)&o,
(void*)&o.in.b - (void*)&o,
(void*)&o.d - (void*)&o);
printf("sizeof(Outer)=%zu\n", sizeof(struct Outer));
return 0;
}
// 輸出: c:0 in.a:4 in.b:8 d:16 sizeof=24
| 陷阱 | 現象 | 對策 |
|---|---|---|
| 默認對齊不同 | 32位 4字節,64位 8字節 | 網絡/文件協議显式 #pragma pack(1) 或手動序列化 |
| 長整型大小不同 | Windows 64位 long=4,Linux 64位 long=8 | 使用 stdint.h 固定寬度類型 (int32_t, int64_t) |
| 指針大小不同 | 32位 4字節,64位 8字節 | 結構體中避免裸指針,用句柄/索引 |
| 位域佈局未定義 | 高低位順序、跨邊界行為 | 位域僅用於本地硬件映射,禁用於跨平台數據交換 |
double, int, short, char。#include ,用 uint32_t 替代 unsigned int。#pragma pack(1) + 手動添加填充字段保證對齊,或使用序列化庫。_Static_assert(sizeof(Struct) == EXPECTED, "layout changed"); (C11)。sizeof(struct) 不是簡單加法,而是**硬件架構、編譯器實現、語言標準**三方博弈的結果。理解對齊本質,善用 #pragma pack、stdint.h、成員排序、靜態斷言,才能寫出高性能、可移植、無驚喜的 C 代碼。