位域(Bit-field)

← C语言知识地图


位域是 C 语言结构体中按 bit 为单位分配内存的语法,打破”以字节为最小单位”的规则。

基本语法

struct Register {
    uint8_t enable   : 1;  // 占 1 bit(范围 0~1)
    uint8_t mode     : 3;  // 占 3 bit(范围 0~7)
    uint8_t priority : 4;  // 占 4 bit(范围 0~15)
};

bit → sizeof(struct Register) == 1 字节。

两种特殊写法

写法作用示例
无名位域 : N保留/跳过 N 个 bit(Reserved)uint8_t : 3;
零长位域 : 0强制下一个成员对齐到下一存储单元边界uint8_t : 0;
// 无名位域:填充保留位
struct {
    uint8_t a : 2;
    uint8_t   : 3;  // 保留 3 bit
    uint8_t b : 3;
};
 
// 零长位域:强制断行
struct Example {
    uint8_t a : 3;
    uint8_t   : 0;  // 下一个成员从新 byte 开始
    uint8_t b : 3;
};

核心应用场景

  1. 极限压缩内存 — 多个布尔标志合并到一个字节,嵌入式/海量数据场景省 RAM
  2. 硬件寄存器映射 — 寄存器按 bit 划分功能,位域直接对应硬件位
// 串口控制寄存器映射
typedef struct {
    uint32_t TXEN  : 1;   // 发送使能
    uint32_t RXEN  : 1;   // 接收使能
    uint32_t BAUDS : 4;   // 波特率选择
    uint32_t       : 2;   // 保留
    uint32_t parity: 2;   // 校验位
} UART_CR_t;

四大坑点

符号陷阱

int 或未显式写 unsigned 声明 1-bit 位域:

struct Flag {
    int is_ok : 1;  // ❌ 有符号!取值范围 -1 和 0,不是 1 和 0
};

铁律:标志位必须用 unsigned intuint8_t/uint32_t

不能取地址

CPU 按字节编址,无法为单个 bit 分配指针:

struct Register reg;
uint8_t *p = &reg.enable;  // ❌ 编译报错

跨平台可移植性差

C 标准不规定字节内 bit 排列顺序(LSB vs MSB)。不同编译器可能把第一个成员放在 bit 0 或 bit 7。禁止直接跨平台传输位域结构体。

性能开销

修改位域时编译器生成”读-改-写”指令序列:读整个字节 → 清零目标 bit → 写入新值 → 写回内存。比普通变量稍慢,本质是空间换时间。