因為記憶體對齊。處理器訪問記憶體時,若資料跨越自然邊界(如 4-byte int 跨 4-byte boundary),需發起兩次總線事務並拼湊,嚴重損效能。編譯器自動插入填充位元組,確保每個成員起始位址滿足其對齊要求。
offset % alignof(成員型別) == 0sizeof(struct) % max_align == 0(max_align 為所有成員中最大對齊數)sizeof 盡可能小struct A {
char a; // 1 byte
short b; // 2 bytes
char c; // 1 byte
int d; // 4 bytes
};
| 成員 | offset | size | 填充 | 說明 |
|---|---|---|---|---|
| a | 0 | 1 | 1 | 下一個 short 需 2-byte 對齊,offset 1→2 需填 1 |
| b | 2 | 2 | 0 | 滿足 2-byte 對齊 |
| c | 4 | 1 | 3 | 下一個 int 需 4-byte 對齊,offset 5→8 填 3 |
| d | 8 | 4 | 0 | 滿足 4-byte 對齊 |
| 總計 | 12 | 4 | sizeof(struct A) == 12 |
struct B { // 重新排序
int d; // 4
short b; // 2
char a; // 1
char c; // 1
}; // sizeof = 8 (無填充)
結論:將大對齊成員放前面、小對齊放後面,往往能減少填充,縮減結構體體積。
#pragma pack(8) // 指定最大對齊 8
struct Inner {
char a; // align 1
long b; // align 4 (假設 long=4)
}; // sizeof(Inner)=8 (max_align=4, 8%4=0)
struct Outer {
char c; // offset 0, align 1
struct Inner in; // offset 4 (需 4-byte 對齊), sizeof=8
long long d; // offset 16 (需 8-byte 對齊), sizeof=8
}; // sizeof(Outer)=24
關鍵點:巢狀結構體視為「一個成員」,其對齊要求 = max(成員對齊, #pragma pack 值),但不超過自身 max_align。
#pragma pack(push, 1) // 保存舊值,設定 1-byte 對齊(緊湊模式)
struct Packed {
char a;
int b;
}; // sizeof = 5 (無填充)
#pragma pack(pop) // 恢復預設對齊
SIGBUS(ARM/SPARC)或顯著降速_Alignas(1)、GCC/Clang __attribute__((packed))#include <stddef.h>
#include <stdio.h>
struct Demo { char a; int b; char c; };
int main(void) {
printf("a: %zu\n", offsetof(struct Demo, a)); // 0
printf("b: %zu\n", offsetof(struct Demo, b)); // 4
printf("c: %zu\n", offsetof(struct Demo, c)); // 8
printf("sizeof: %zu\n", sizeof(struct Demo)); // 12
return 0;
}
struct Flags {
unsigned int f1 : 1;
unsigned int f2 : 3;
unsigned int : 0; // 強制下一個位欄從新的儲存單元開始
unsigned int f3 : 4;
};
unsigned int 或 _Alignas 指定型別):0 強制對齊到下一個單元邊界| 場景 | 對齊依據 | 典型 sizeof |
|---|---|---|
| 基本型別 | alignof(type) (通常等於 size) | 1,2,4,8,16 |
| 結構體 | max(成員對齊) 受 #pragma pack 限制 | 按規則計算 |
| 聯合體 | max(成員對齊) | max(成員 size) 向上對齊 |
| 陣列 | 元素對齊 | 元素 size * N (已對齊) |
「成員各自對齊,結構整體對齊,填充最小化;
大型前排小型後,重排省空間;
pack 能改最大對齊,位欄不跨單元,跨平台慎用它。」