位域(Bit-field)是C語言特有的結構成員,允許以位為單位指定成員寬度,將一個位元組/字內的多個位打包儲存。適用於:
struct 結構標籤 {
型別說明符 成員名 : 位寬;
// ...
} 變數列表;
int, signed int, unsigned int, _Bool (C99)。某些編譯器擴展支援 long, long long。int 通常16/32位,位寬不得超過該值(標準要求 ≤ 型別位寬)。int 大小)。unsigned : n; 佔位不可存取。&bf 非法),因為它們不佔獨立位址空間。struct bs {
unsigned int a : 8; // 佔8位
unsigned int b : 2; // 佔2位
unsigned int c : 6; // 佔6位,共16位 = 1個int(假設16位系統)
} data;
struct example {
unsigned a : 4; // 第1個int的位0-3
unsigned : 0; // 強制對齊到下一個int邊界(剩餘12位廢棄)
unsigned b : 4; // 第2個int的位0-3
unsigned c : 4; // 第2個int的位4-7
}; // 總計佔2個int = 4位元組
:0 特殊語法:強制下一位域從新儲存單元開始。
struct k {
int a : 1; // 位0
int : 2; // 位1-2 填充/保留,不可存取
int b : 3; // 位3-5
int c : 2; // 位6-7
};
存取語法同一般結構成員:變數.成員 或 指標->成員。支援所有整數運算與位運算。
#include <stdio.h>
int main() {
struct bs {
unsigned a : 1;
unsigned b : 3;
unsigned c : 4;
} bit, *pbit;
bit.a = 1; // 最大值1
bit.b = 7; // 最大值7 (3位)
bit.c = 15; // 最大值15 (4位)
printf("初始: %d, %d, %d\n", bit.a, bit.b, bit.c);
pbit = &bit;
pbit->a = 0; // 指標存取
pbit->b &= 3; // 位運算:7 & 3 = 3 (0111 & 0011 = 0011)
pbit->c |= 1; // 位運算:15 | 1 = 15 (1111 | 0001 = 1111)
printf("修改後: %d, %d, %d\n", pbit->a, pbit->b, pbit->c);
return 0;
}
/* 某MCU UART控制暫存器 (16位) */
typedef union {
struct {
unsigned enable : 1; // bit 0
unsigned loopback : 1; // bit 1
unsigned parity_en : 1; // bit 2
unsigned parity_sel : 1; // bit 3
unsigned data_bits : 2; // bit 4-5: 0=5b,1=6b,2=7b,3=8b
unsigned stop_bits : 1; // bit 6: 0=1bit, 1=2bit
unsigned reserved : 9; // bit 7-15 保留
} bits;
uint16_t reg;
} UART_CTRL_REG;
/* 使用 */
volatile UART_CTRL_REG *uart_ctrl = (volatile UART_CTRL_REG*)0x4000C000;
uart_ctrl->bits.enable = 1;
uart_ctrl->bits.data_bits = 3; // 8 data bits
uart_ctrl->bits.parity_en = 1;
uart_ctrl->bits.parity_sel = 0; // even parity
// 整寫也可:uart_ctrl->reg = 0x002D;
/* CAN 2.0A 標準幀標頭 (11位ID + RTR + IDE + r0 + DLC) */
typedef struct {
unsigned id : 11; // 標準識別碼
unsigned rtr : 1; // 遠端傳輸請求
unsigned ide : 1; // 識別碼擴展位 (0=標準幀)
unsigned r0 : 1; // 保留位
unsigned dlc : 4; // 資料長度碼 (0-8)
} CAN_STD_HEADER;
/* 從緩衝區解析 */
uint8_t buf[2];
CAN_STD_HEADER *hdr = (CAN_STD_HEADER*)buf;
printf("ID=0x%X, DLC=%d\n", hdr->id, hdr->dlc);
struct file_flags {
unsigned readable : 1;
unsigned writable : 1;
unsigned executable : 1;
unsigned hidden : 1;
unsigned system : 1;
unsigned archive : 1;
unsigned reserved : 2;
}; // 僅佔1個位元組,替代6個bool變數(6-24位元組)
| 議題 | 標準規定 | 實作差異 |
|---|---|---|
| 位域分配順序 | 實作定義 | LSB到MSB 或 MSB到LSB(位元組大小端相關) |
| 跨儲存單元處理 | 實作定義 | 自動跳到下一單元 vs 允許跨界 |
| 有號位域負值表示 | 實作定義 | 二補數/一補數/符號大小 |
| 位域對齊要求 | 實作定義 | 不同編譯器對齊策略不同 |
unsigned 型別 避免有號位域陷阱。uint8_t, uint16_t, uint32_t (C99 stdint.h)。union 實現整數↔位域雙向存取(如暫存器映射範例)。static_assert(sizeof(struct) == expected, "layout changed") (C11)。/* 強制封裝,無填充 */
#pragma pack(push, 1)
struct packed_reg {
unsigned a : 3;
unsigned b : 5;
};
#pragma pack(pop)
/* 或使用屬性 */
struct __attribute__((packed)) packed_reg { ... };
/* 指定儲存單元型別 */
struct {
uint32_t a : 8;
uint32_t b : 8;
} __attribute__((packed));
| 維度 | 位域 | 手動位運算 |
|---|---|---|
| 可讀性 | 高(命名成員) | 低(魔術數字、移位遮罩) |
| 型別安全 | 編譯器檢查範圍 | 無檢查 |
| 可移植性 | 低(佈局實作定義) | 高(明確控制每一位) |
| 效能 | 編譯器優化,通常等價 | 完全可控 |
| 除錯支援 | 除錯器顯示成員名/值 | 需手動解碼 |
| 適用場景 | 內部資料結構、暫存器映射 | 網路封包、檔案格式、跨平台序列化 |
位域是C語言表達領域模型的語法糖,讓位級操作具備結構化可讀性。核心價值在於程式碼清晰度而非效能(現代編譯器對手動位運算優化極佳)。嚴禁用於跨平台資料交換格式。掌握union配合、:0對齊、unsigned型別、靜態斷言等技巧,即可安全發揮其優勢。