目录
位运算基础说明
位运算直接对整数内存里的二进制比特 (bit)进行操作,float、double浮点数不能使用位运算,只能用 char /unsigned char /int/unsigned int 这类整型。
C 语言一共 6 个位运算符:
表格
| 运算符 | 名称 | 作用 |
|---|---|---|
& | 按位与 | 两个 bit 都为 1,结果才为 1 |
| | 按位或 | 任意一个 bit 为 1,结果就是 1 |
^ | 按位异或 | 两个 bit 不一样则为 1,相同为 0 |
~ | 按位取反 | 单目运算符,0 变 1,1 变 0 |
<< | 左移 | 所有比特向左移动,右侧补 0 |
>> | 右移 | 所有比特向右移动,分逻辑右移、算术右移 |
⚠️注意区分
&按位与 vs&&逻辑与|按位或 vs||逻辑或
位运算:操作每一个二进制位; 逻辑运算:只判断真 / 假(非 0 为真,0 为假)。
示例:
c
unsigned char x = 0b00000101; //5
unsigned char y = 0b00000110; //6
问题
- float 浮点变量可以直接做按位与
&运算吗? &&和&二者有什么本质区别?- 位运算操作的是数据的什么?
:
1.不可以
2.一个是逻辑上的 另一个是对比特位的操作
3.bit
:
批改:
- ✅不可以,位运算只支持整型,float 禁止直接位运算。
- ✅
&&逻辑与:判断真假;&按位与:逐比特运算。 - ✅bit(二进制位)。
按位与 &、按位或 |
按位与 &
逐位对比:两个比特都等于 1,结果 bit 才是 1,否则为 0。 示例(8bit)
plaintext
a = 1011 0101
b = 0000 1111
a & b = 0000 0101
核心用途:掩码清零。 掩码中想要清零的位写 0,保留的位写 1。
变量 & 掩码,就可以把指定比特清 0,其余位保持不变。
按位或 |
逐位对比:任意一个比特为 1,结果 bit 就是 1。
plaintext
a = 1011 0101
b = 0000 1111
a | b = 1011 1111
核心用途:置位(把某些 bit 强制设为 1)。 掩码需要置 1 的位写 1,不变位写 0。
变量 | 掩码,指定比特变成 1,其余位不变。
嵌入式寄存器高频套路
- 想要把某几个 bit 清 0:
reg = reg & (~mask); - 想要把某几个 bit 置 1:
reg = reg | mask;
表格
| 运算 | 核心作用 | 寄存器常用场景 | |
|---|---|---|---|
&按位与 | 选择性清零 bit | 清除寄存器某些控制位 | |
| ` | ` 按位或 | 选择性置 1 bit | 打开寄存器某些功能位 |
问题
- 掩码
0b00001111,0b11011010 & 0b00001111的结果二进制是多少? - 如果只想把变量第 2、3 比特设置为 1,其余位保持不变,使用什么运算?
reg & mask,掩码里面写 0 的比特,对原变量起到什么效果?
:
1.0b00001010
2.|
3.对应的比特会被清零
:
批改:
- ✅
0b00001010,高 4 位被掩码 0 清零,保留低 4 位。 - ✅
|按位或。 - ✅掩码为 0 的比特位,运算后对应 bit 清零。
按位异或 ^ 、按位取反 ~
按位异或 ^
规则:对应比特,值不相同则结果为 1;相同则结果为 0。
plaintext
a = 1011 0101
mask = 0000 1111
a ^ mask = 1011 1010
核心用途:翻转指定比特 掩码想要翻转的 bit 写 1,保持不变的 bit 写 0。
变量 ^ mask。 掩码位为 1 → 原 bit 翻转;掩码位为 0 → 原 bit 保持原样。
按位取反 ~
单目运算符,只需要一个操作数,把全部比特 0 变 1,1 变 0。
⚠️重点坑点:
~按照变量完整宽度取反,不是只看你写的 8 位字面量。unsigned char val = 0b00001111;~val,内部运算会提升为 int,全部比特反转;赋值回 unsigned char 才截断 8bit。
嵌入式常用组合:reg = reg & (~mask); ~mask把掩码取反,用来配合按位与,清零指定若干 bit。
表格
| 运算 | 核心作用 | 寄存器场景 |
|---|---|---|
^按位异或 | 翻转选定 bit | 翻转寄存器某些状态位 |
~按位取反 | 全部比特按位反转 | 生成反向掩码,配合 & 做清零 |
问题
0b11010010 ^ 0b00001111结果二进制是多少?- 想要翻转变量的低 4 位,高 4 位不变,用什么运算?
~是双目运算符还是单目运算符?
:
1.11011101
2. ~
3.单目
:
批改:
1.✅0b11011101,低 4 位全部翻转,高 4 位保留。
2.❌,应该是 **^异或 **。变量 ^ 0b00001111完成低 4 位翻转;~是把所有比特全部取反,不能只翻转一部分。
3.✅~属于单目运算符,只接收一个操作数。
补充小记:
|:置 1&:清零^:翻转~:整体全部按位取反
移位运算符 << 左移、>> 右移
左移 <<
所有比特整体向左移动 N 位,右侧空位补 0;左侧移出的比特直接丢弃。 示例:
plaintext
x = 0b00000101
x <<1 = 0b00001010
x <<2 = 0b00010100
无符号数左移 n 位等价于 ×\(2^n\)。
嵌入式用途:快速构造掩码,
1U << 3,得到第 3 位为 1 的掩码。
右移 >>
- unsigned 无符号数:逻辑右移,比特右移,左边空位补
0。 - signed 有符号数:算术右移,左边补符号位(正数补 0,负数补 1),标准没有强制规定,不同编译器行为可能不一致。
✅嵌入式写寄存器,一律使用 unsigned 无符号类型,规避有符号右移的未确定行为。
示例(无符号 8bit)
plaintext
x = 0b10000100
x >> 1 = 0b01000010
表格
| 运算 | 行为 | 注意事项 |
|---|---|---|
<<左移 | 左移,右侧补 0 | 移出高位直接丢弃 |
>>无符号右移 | 逻辑右移,左侧补 0 | 行为确定,单片机推荐 |
>>有符号右移 | 算术右移,补符号位 | 可移植性差,硬件代码尽量不用 |
问题
unsigned char a = 0b00001001;a << 2二进制结果是多少?- 单片机配置掩码,
1U << 5得到的掩码二进制是什么? - C 语言写寄存器代码,为什么尽量避免使用 signed 有符号类型做右移?
:
1.0b00100100
2.0b00100000
3.符号的问题
:
批改:
1. ✅`0b00100100`,向左移动2位,右侧补0。
2. ✅`0b00100000`,第5位置1。
3. ✅完整表述:有符号数右移是算术右移,填充符号位,行为标准没有完全强制,存在可移植性问题,硬件寄存器代码要规避不确定行为。 有没有哪里没理解?有疑问可以提,确认无疑惑开启下一段。
嵌入式寄存器标准位运算套路
前面单独学完 & | ^ ~ << >>,现在组合起来,就是单片机操作寄存器最常用四套模板。
约定:bit 编号从0 开始,最低位是 bit0。
- 把第 n 位设置为 1(置位)
c
reg |= (1U << n);
1U << n 构造只有第 n 位为 1 的掩码,按位或,只把目标 bit 置 1,其余位保持不变。
- 把第 n 位清零
c
reg &= ~(1U << n);
1U << n造出掩码;~取反,目标 bit 变为 0 其余全 1;按位与,只清零目标 bit。
- 翻转第 n 位
c
reg ^= (1U << n);
异或掩码,目标 bit 0 变 1、1 变 0,其余 bit 不变。
- 读取第 n 位是 0 还是 1
c
(reg >> n) & 1U;
把寄存器右移 n 位,目标 bit 挪到最低位;再&1U,取出该 bit 的值。
⚠️重要细节:写
1U不要写1,用无符号字面量,规避有符号类型带来移位、取反的坑。
表格
| 功能 | 代码模板 | |
|---|---|---|
| 置第 n 位为 1 | `reg | = (1U << n)` |
| 清第 n 位为 0 | reg &= ~(1U << n) | |
| 翻转第 n 位 | reg ^= (1U << n) | |
| 读第 n 位 | (reg >> n) & 1U |
问题
- 需要把寄存器第 4 bit 置 1,写出对应 C 语句。
- 需要读取寄存器第 3 位状态,写出对应 C 语句。
reg &= ~(1U<<6);这条语句完成什么功能?
:
- 寄存器第 4bit 置 1:
c
reg |= (1U << 4);
- 读取寄存器第 3 位状态:
c
(reg >> 3) & 1U;
reg &= ~(1U<<6);
功能:将寄存器第 6 位清零,其余比特保持不变。
多比特操作 & 高频坑点
前面示例大多操作单个 bit,实际寄存器经常要同时操作连续多个比特。
构造多比特掩码示例: 想要同时操作 bit2、bit3:二进制 0b11 << 2,等价 0x3U << 2。
c
unsigned int mask = (0x3U << 2);
reg |= mask; // 将bit2、bit3同时置1,其余位不变
reg &= ~mask; // 将bit2、bit3同时清零,其余位不变
⚠️致命坑 1:禁止直接赋值覆盖寄存器
c
reg = 0x04; // ❌错误!直接覆盖寄存器全部位,其他控制位全部丢失
reg |= (1U << 2); // ✅正确,只修改目标位,保留其余原有硬件状态
⚠️致命坑 2:移位位数不能大于等于变量总 bit 宽度
unsigned char是 8bit,x << 8,移位 8 位,C 语言标准属于未定义行为,结果不可预测。
⚠️致命坑 3:尽量使用无符号后缀
U~(1 << 31),1 是有符号 int,会出现符号扩展异常;写成~(1U << 31)才安全。
表格
| 错误写法 | 问题 | 正确写法 | |
|---|---|---|---|
reg = 0x08; | 覆盖寄存器全部 bit | `reg | = (1U << 3)` |
x << 8(unsigned char) | 移位 >= 位宽,未定义行为 | 控制移位次数小于类型 bit 数 | |
~(1 << 5) | 1 为有符号,符号扩展风险 | ~(1U <<5) |
问题
- 需要同时把 bit5、bit6 置 1,其余位保持不变,写出代码。
unsigned char var; var << 8;会出现什么问题?- 修改寄存器,为什么不推荐直接
reg = 某数值?
:
- 同时把 bit5、bit6 置 1,其余位不变:
c
reg |= ((1U << 5) | (1U << 6));
// 也可以写成 reg |= (0x3U << 5);
unsigned char var; var << 8;
unsigned char为 8 位,移位次数等于类型位宽,属于C 语言未定义行为,结果不可预测。
- 不推荐直接
reg = 某数值:
会直接覆盖寄存器全部比特,把原本其他功能控制位全部改写丢失,破坏原有硬件状态。
小结
核心知识回顾
- 位运算只作用于整型,float/double 不能参与;区分
&和&&,|和||。 &:按位与,配合掩码清零;|:按位或,置 1 比特;^:异或,翻转比特;~:全部比特取反。<<左移右侧补 0;>>无符号逻辑右移补 0;有符号算术右移存在可移植风险,硬件代码规避。- 单片机寄存器四条标准模板,优先使用
1U无符号字面量。 - 不要直接
reg = value覆盖全部位;移位次数不能大于等于变量总比特数。
和前面知识点串联 外设寄存器 + volatile + 位运算,三者组合,就是 STM32 底层寄存器配置代码的完整基础。

2508

被折叠的 条评论
为什么被折叠?



