位域 是 C 語言為節省記憶體、直接操作硬件寄存器提供的特殊機制。它將結構體成員按位而非字節分配,廣泛應用於嵌入式寄存器映射、網絡協議頭部解析、標誌位壓縮存儲等場景。本文系統講解位域定義規則、存儲佈局、使用技巧與工程避坑指南。
struct 位域結構名 {
類型說明符 位域名 : 位域長度;
// ...
};
struct bs {
int a : 8; // 佔 8 位
int b : 2; // 佔 2 位
int c : 6; // 佔 6 位
} data; // 共 16 位 = 2 字節
int 為 16/32 位)。:0 強制換行到下一單元。int、unsigned int、signed int、_Bool (C99) 支援,其他類型視實現定義。struct bs {
unsigned a : 4;
unsigned : 0; // 強制從下一個存儲單元開始
unsigned b : 4; // 存放在下一單元低 4 位
unsigned c : 4;
};
struct k {
int a : 1;
int : 2; // 2 位填充,不可訪問
int b : 3;
int c : 2;
};
位域成員訪問語法與普通結構體一致:var.member、ptr->member。
struct bs {
unsigned a : 1;
unsigned b : 3;
unsigned c : 4;
} bit, *pbit;
int main() {
bit.a = 1; // 最大值 1 (1 位)
bit.b = 7; // 最大值 7 (3 位)
bit.c = 15; // 最大值 15 (4 位)
printf("%d,%d,%d\n", bit.a, bit.b, bit.c); // 1,7,15
pbit = &bit;
pbit->a = 0; // 指針賦值
pbit->b &= 3; // 復合位運算:7 & 3 = 3
pbit->c |= 1; // 復合位運算:15 | 1 = 15
printf("%d,%d,%d\n", pbit->a, pbit->b, pbit->c); // 0,3,15
return 0;
}
// 某 MCU GPIO 控制寄存器 32 位
struct GPIO_CR {
unsigned MODE0 : 2; // 00=輸入 01=輸出 10=複用 11=模擬
unsigned MODE1 : 2;
// ... MODE15
unsigned OTYPE : 16; // 輸出類型 0=推挽 1=開漏
unsigned OSPEED : 16; // 輸出速度
unsigned PUPD : 16; // 上拉/下拉
unsigned IDR : 16; // 輸入數據 (唯讀)
unsigned ODR : 16; // 輸出數據
unsigned BSRR : 32; // 位置位/復位
unsigned LCKR : 16; // 配置鎖定
} __attribute__((packed)); // 禁止編譯器填充
// 使用
volatile struct GPIO_CR *GPIOA = (void*)0x40020000;
GPIOA->MODE5 = 1; // PA5 設為輸出
GPIOA->ODR |= (1<<5); // PA5 輸出高電平
// IPv4 頭部 (20 字節基本)
struct IPv4_Hdr {
unsigned version : 4;
unsigned ihl : 4;
unsigned dscp : 6;
unsigned ecn : 2;
unsigned total_len : 16;
unsigned ident : 16;
unsigned flags : 3;
unsigned frag_off: 13;
unsigned ttl : 8;
unsigned proto : 8;
unsigned checksum: 16;
uint32_t src_ip;
uint32_t dst_ip;
} __attribute__((packed));
sizeof(struct) 可能超出預期。&bit.a 是非法的,位域無地址。int x:3 範圍是 -4..3 而非 0..7,賦值 7 會溢出為 -1。
// C++20 std::bit_field / 手工位操作宏
#define GET_BIT(reg, pos) (((reg) >> (pos)) & 1)
#define SET_BIT(reg, pos) ((reg) |= (1 << (pos)))
#define CLR_BIT(reg, pos) ((reg) &= ~(1 << (pos)))
#define GET_BITS(reg, hi, lo) (((reg) >> (lo)) & ((1 << ((hi)-(lo)+1)) - 1))
#define SET_BITS(reg, hi, lo, val) \
do { (reg) = ((reg) & ~(((1 << ((hi)-(lo)+1)) - 1) << (lo))) | ((val) << (lo)); } while(0)
// 類型安全封裝
template
struct BitField {
static constexpr unsigned mask = ((1u << (Hi-Lo+1)) - 1) << Lo;
static unsigned get(unsigned reg) { return (reg & mask) >> Lo; }
static void set(unsigned& reg, unsigned val) { reg = (reg & ~mask) | ((val << Lo) & mask); }
};
// 使用
BitField<7,4>::set(reg, 0xA); // 設定 bit 7-4 = 0xA
位域是以可移植性換取記憶體緊湊的妥協產物。在單平台嵌入式寄存器映射、本地標誌位壓縮場景極其好用;但跨平台數據交換、網絡協議解析、多線程共享場景請改用顯式位運算宏/模板。掌握存儲規則、無名填充、指針操作、有符號陷阱、未定義行為邊界,才能在需要時果斷使用、不需要時堅決避開。