結構體位字段允許以「位」為單位分配成員寬度,是嵌入式開發、硬件寄存器映射、網絡協議解析中節省內存、對齊硬件位定義的利器。但其行為深受編譯器、架構、位序影響,充滿「未定義行為」陷阱。本文通過實測代碼,剖析位字段的內存佈局、溢出截斷、跨平台差異。
struct a {
int x:1; // 1 位
int y:2; // 2 位
int z:33; // 33 位 (超過 int 32 位)
};
int main() {
struct a d;
d.z = d.x + d.y; // 位字段參與運算會提升為 int
printf("%p %d %d %d %zu\n", &d, d.x, d.y, d.z, sizeof(d));
return 0;
}
輸出結果:
0x22fed4 0 0 0 12
x:1 + y:2 = 3 位,編譯器將其打包進一個 4 字節 (32 位) 的 int 存儲單元。z:33 超過 32 位,無法塞入同一個 int,編譯器另起一個存儲單元。因 int 通常 4 字節,33 位需 8 字節 (64 位) 或兩個 int (視實現而定)。sizeof(d) = 12 字節:第一單元 4 字節 + 第二單元 8 字節 (或 4+4 視對齊)。z:33 實際僅能存儲低 32 位 (若基類型為 int) 或低 33 位 (若編譯器支持 64 位位字段)。d.z = d.x + d.y 時,右側運算結果為 int (0),截取低位存入,故 d.z = 0。int x:1 是有符號 1 位,取值範圍 -1 ~ 0 (非 0~1)。int 或 unsigned int,再進行運算。d.x=0, d.y=0,相加為 0,無符號擴展問題。unsigned int 或 signed int 顯式聲明,避免 int 符號性實現定義。int 位字段 ≤ 32,需更大用 long long 或分拆多字段。static_assert 驗證 sizeof 與偏移量。>>, &, | 顯式組裝/解析,而非依賴位字段佈局。位字段是「雙刃劍」:用得好能極致壓縮內存、完美映射硬件;用不好成跨平台移植噩夢。原則:內部實現用位字段,對外接口用顯式位運算。 理解其背後的存儲單元劃分、對齊填充、整數提升機制,才能駕馭這把手術刀。