位运算总结

目录

位运算基础说明

按位与 &、按位或 |

按位异或 ^ 、按位取反 ~

移位运算符 << 左移、>> 右移

嵌入式寄存器标准位运算套路

多比特操作 & 高频坑点

小结


位运算基础说明

位运算直接对整数内存里的二进制比特 (bit)进行操作,floatdouble浮点数不能使用位运算,只能用 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

问题

  1. float 浮点变量可以直接做按位与&运算吗?
  2. &&& 二者有什么本质区别?
  3. 位运算操作的是数据的什么?

1.不可以

2.一个是逻辑上的 另一个是对比特位的操作

3.bit

批改:

  1. ✅不可以,位运算只支持整型,float 禁止直接位运算。
  2. &&逻辑与:判断真假;&按位与:逐比特运算。
  3. ✅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打开寄存器某些功能位

问题

  1. 掩码 0b000011110b11011010 & 0b00001111 的结果二进制是多少?
  2. 如果只想把变量第 2、3 比特设置为 1,其余位保持不变,使用什么运算?
  3. reg & mask,掩码里面写 0 的比特,对原变量起到什么效果?

1.0b00001010

2.|

3.对应的比特会被清零

批改:

  1. 0b00001010,高 4 位被掩码 0 清零,保留低 4 位。
  2. | 按位或。
  3. ✅掩码为 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翻转寄存器某些状态位
~按位取反全部比特按位反转生成反向掩码,配合 & 做清零

问题

  1. 0b11010010 ^ 0b00001111 结果二进制是多少?
  2. 想要翻转变量的低 4 位,高 4 位不变,用什么运算?
  3. ~是双目运算符还是单目运算符?

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行为确定,单片机推荐
>>有符号右移算术右移,补符号位可移植性差,硬件代码尽量不用

问题

  1. unsigned char a = 0b00001001a << 2二进制结果是多少?
  2. 单片机配置掩码,1U << 5得到的掩码二进制是什么?
  3. C 语言写寄存器代码,为什么尽量避免使用 signed 有符号类型做右移?

1.0b00100100

2.0b00100000

3.符号的问题

批改:

1. ✅`0b00100100`,向左移动2位,右侧补0。

2. ✅`0b00100000`,第5位置1。

3. ✅完整表述:有符号数右移是算术右移,填充符号位,行为标准没有完全强制,存在可移植性问题,硬件寄存器代码要规避不确定行为。 有没有哪里没理解?有疑问可以提,确认无疑惑开启下一段。


嵌入式寄存器标准位运算套路

前面单独学完 & | ^ ~ << >>,现在组合起来,就是单片机操作寄存器最常用四套模板。

约定:bit 编号从0 开始,最低位是 bit0。

  1. 把第 n 位设置为 1(置位)

c

reg |= (1U << n);

1U << n 构造只有第 n 位为 1 的掩码,按位或,只把目标 bit 置 1,其余位保持不变。

  1. 把第 n 位清零

c

reg &= ~(1U << n);

1U << n造出掩码;~取反,目标 bit 变为 0 其余全 1;按位与,只清零目标 bit。

  1. 翻转第 n 位

c

reg ^= (1U << n);

异或掩码,目标 bit 0 变 1、1 变 0,其余 bit 不变。

  1. 读取第 n 位是 0 还是 1

c

(reg >> n) & 1U;

把寄存器右移 n 位,目标 bit 挪到最低位;再&1U,取出该 bit 的值。

⚠️重要细节:写1U不要写1,用无符号字面量,规避有符号类型带来移位、取反的坑。

表格

功能代码模板
置第 n 位为 1`reg= (1U << n)`
清第 n 位为 0reg &= ~(1U << n)
翻转第 n 位reg ^= (1U << n)
读第 n 位(reg >> n) & 1U

问题

  1. 需要把寄存器第 4 bit 置 1,写出对应 C 语句。
  2. 需要读取寄存器第 3 位状态,写出对应 C 语句。
  3. reg &= ~(1U<<6); 这条语句完成什么功能?

  1. 寄存器第 4bit 置 1:

c

reg |= (1U << 4);
  1. 读取寄存器第 3 位状态:

c

(reg >> 3) & 1U;
  1. 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)

问题

  1. 需要同时把 bit5、bit6 置 1,其余位保持不变,写出代码。
  2. unsigned char var; var << 8; 会出现什么问题?
  3. 修改寄存器,为什么不推荐直接 reg = 某数值

  1. 同时把 bit5、bit6 置 1,其余位不变:

c

reg |= ((1U << 5) | (1U << 6));
// 也可以写成 reg |= (0x3U << 5);
  1. unsigned char var; var << 8;

unsigned char为 8 位,移位次数等于类型位宽,属于C 语言未定义行为,结果不可预测。

  1. 不推荐直接reg = 某数值

会直接覆盖寄存器全部比特,把原本其他功能控制位全部改写丢失,破坏原有硬件状态。

小结

核心知识回顾

  1. 位运算只作用于整型,float/double 不能参与;区分 &&&|||
  2. &:按位与,配合掩码清零;|:按位或,置 1 比特;^:异或,翻转比特;~:全部比特取反。
  3. <<左移右侧补 0;>>无符号逻辑右移补 0;有符号算术右移存在可移植风险,硬件代码规避。
  4. 单片机寄存器四条标准模板,优先使用1U无符号字面量。
  5. 不要直接reg = value覆盖全部位;移位次数不能大于等于变量总比特数。

和前面知识点串联 外设寄存器 + volatile + 位运算,三者组合,就是 STM32 底层寄存器配置代码的完整基础。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值