位域(Bit-field)是 C 語言獨特的結構體成員宣告方式,允許指定成員佔用的位元寬度,實現位元級記憶體佈局控制。廣泛應用於嵌入式系統、硬體暫存器對應、網路封包解析、旗標儲存等場景。本文詳細說明語法、記憶體佈局規則、跨平台考量與實務範例。
在結構體中宣告位域:
struct {
型別 成員名稱 : 位元寬度;
} 變數名;
int、signed int、unsigned int(C99 允許 _Bool)unsigned int 通常為 32 位)傳統結構體儲存兩個布林旗標需 8 位元組:
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status1; // sizeof = 8 bytes
使用位域僅需 4 位元組(實際僅用 2 位元):
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2; // sizeof = 4 bytes
驗證程式:
#include <stdio.h>
#include <string.h>
struct { unsigned int widthValidated; unsigned int heightValidated; } status1;
struct { unsigned int widthValidated : 1; unsigned int heightValidated : 1; } status2;
int main() {
printf("Memory size occupied by status1 : %zu\n", sizeof(status1));
printf("Memory size occupied by status2 : %zu\n", sizeof(status2));
return 0;
}
輸出:
Memory size occupied by status1 : 8 Memory size occupied by status2 : 4
3 位元位域可儲存 0~7:
struct { unsigned int age : 3; } Age;
超出範圍賦值行為:
#include <stdio.h>
struct { unsigned int age : 3; } Age;
int main() {
Age.age = 4; printf("Age.age : %d\n", Age.age); // 4
Age.age = 7; printf("Age.age : %d\n", Age.age); // 7
Age.age = 8; printf("Age.age : %d\n", Age.age); // 0 (截斷溢位)
return 0;
}
編譯會產生警告,執行時高位元被截斷(8 = 0b1000 → 取低 3 位 = 0b000 = 0)。
int 邊界。若剩餘空間不足,編譯器會跳至下一個 int 起始位置。unsigned : 0; 強制下一位域從新 int 開始。struct bs {
unsigned a : 4;
unsigned : 4; // 無名填充 4 位
unsigned b : 4; // 從下一個 int 開始
unsigned c : 4;
};
此結構佔用 8 位元組(兩個 unsigned int)。
位域變數可取址,但不可取得位域成員的位址(&bit.a 非法)。可透過結構體指標操作:
struct bs { unsigned a:1; unsigned b:3; unsigned c:4; } bit, *pbit;
pbit = &bit;
pbit->a = 0;
pbit->b &= 3; // 位元 AND 指派
pbit->c |= 1; // 位元 OR 指派
| 場景 | 說明 |
|---|---|
| 硬體暫存器映射 | 對應 MCU 狀態暫存器各位元定義 |
| 網路協定標頭 | IP/TCP 標頭欄位位元級解析 |
| 旗標集合 | 多個布林旗標壓縮儲存 |
| 資料壓縮 | 固定範圍整數緊湊編碼 |
| 檔案格式解析 | 非位元組對齊格式(如 9 位元整數) |
&、|、<<、>>)配合 volatile 明確控制sizeof 回傳整個結構體大小,非位域總位元數位域提供位元級記憶體控制能力,是嵌入式與系統程式設計的利器。但其實作相依特性要求開發者深度理解目標平台 ABI,並在跨平台場景謹慎使用。現代 C 開發中,若非硬體映射或極致記憶體優化需求,建議優先考慮標準位元運算以確保可移植性。