reg |= (1U << bit);清零 → reg &= ~(1U << bit)(掩码必须取反);翻转 → reg ^= (1U << bit);取值 → (reg >> bit) & 1U1U << bit,区别只在运算符和取反一句话:"或放 1、与非放 0、异或翻转、移位读位。"
位操作就四种"掩码 + 运算符"组合:置位 = 或,清零 = 与非,翻转 = 异或,取值 = 右移+与。掩码统一 (1U << bit)。宏封装 = 编译期展开无调用开销,位的位置完全由你控制。
#define SET_BIT(reg, bit) ((reg) |= (1U << (bit))) → 或 1 置位,或 0 保留#define CLEAR_BIT(reg, bit) ((reg) &= ~(1U << (bit))) → 与 0 清 0,与 1 保留。⚠️ 必须取反 ~(1<<bit)#define TOGGLE_BIT(reg, bit) ((reg) ^= (1U << (bit))) → 异或 1 翻转,异或 0 保持#define READ_BIT(reg, bit) (((reg) >> (bit)) & 1U) → 右移目标位到最低位再 &1REG &= (1 << 3); 想清零第 3 位 → 错!把第 3 位以外全清 0SET_BIT(A|B,2) 展开成 A|B |= ... 语法错)| 操作 | 运算符 | 掩码形式 | 口诀 |
|---|---|---|---|
| 置位 | \| |
1 << bit |
或 1 置位 |
| 清零 | & |
~(1 << bit) |
与 0 清零 |
| 翻转 | ^ |
1 << bit |
异或 1 翻转 |
| 取值 | >> + & |
(x >> bit) & 1 |
右移再与 1 |
REG &= (1 << 3); 清零第 3 位 → 不对,需取反 ~(1<<3),否则把第 3 位以外全清零REG |= (1U<<2) | (1U<<7);if (REG & (1<<4)) 和 if ((REG>>4)&1) 作为布尔条件等价;作为值不等价(前者得 0x10 不是 1)。经典坑 if ((REG & (1<<4)) == 1) 永远为假1U 是无符号 1。① 防符号位溢出:1<<31 在 int 下移进符号位是未定义行为,1U<<31 合法且明确是最高位 ② 无符号右移是逻辑移位(补 0),行为可预期inline 函数((1U<<w)-1) << s → REG = (REG & ~掩码) | ((newVal & 掩码) << s)。原理:先擦黑板(清零目标位,其他保留),再写新字(新值限宽+搬到目标位)。(newVal & 掩码) 是限宽(防止高位溢出污染相邻位),<< s 是移位(从 bit0== 1 而非 == 掩码/!=0;位号从 0 开始从右往左数| 相邻概念 | 和本主题的关系 | 孤立理解会犯的错 |
|---|---|---|
| define vs typedef | 位操作宏就是 define 的应用,文本替换+加括号同一套 | 宏忘加括号、忘取反 |
| 位域 | 位域(声明式)vs 位操作宏(手动可控) | 以为位域能替代宏操作寄存器 |
| 寄存器操作 | SET_BIT/CLEAR_BIT 直接用在寄存器上 | 寄存器位操作也忘字节序/volatile |
| HAL 库 | HAL 内部就是位操作宏实现的 | 以为用库就不用懂位操作 |
2026-08-03
每主题一页,复习时只翻本目录。