在现代编程里,位运算常被贴上「高深」「晦涩」的标签,然而它们本质上不过是 CPU 与内存最直接的对话方式。当你写下一行 a << 1 时,实际上在命令处理器「把这条二进制数整体左移一位」,其执行效率比乘法指令高出数个数量级。本文将带你从零拆解六大经典位运算,解锁高效代码、算法加速与底层优化的隐藏技巧。
位运算六维透视
1. 按位与(&):1+1=1,其余为 0
用于清零指定位或提取掩码
uint8_t flags = 0b10101100;
if (flags & 0b00001000) { // 检查第四 bit 是否为 1
// do something
}2. 按位或(|):一球进网就得分
用于置位或合并多标志位
flags |= 0b00000010; // 把第 2 位强行拉成 13. 按位异或(^):不同则真
快速交换变量的经典场景即用 XOR
x ^= y; y ^= x; x ^= y; // x 与 y 完成无临时变量交换4. 按位取反(~):0变1,1变0
常结合掩码清除某些位
uint32_t clearUpper16 = value & ~0xFFFF0000;5. 左移(<<):低位补零,等效乘 2ⁿ
int quickMul8 = num << 3; // 比 num * 8 更快6. 右移(>>):逻辑右移高位补零,算术右移高位补符号
👉 用一条指令让数组长度减半
右移 1 位常用于二分查找中的中值计算,避免溢出的同时保持高性能。
实战场景:让位运算解决日常难题
校验奇偶:一行代码称霸
boolean isEven(int n) { return (n & 1) == 0; }利用最低位即可秒杀 % 2 运算。
压缩存储:把 32 个布尔塞进一个 int
// 设置第 k 个开关
void turnOn(uint32_t *reg, int k) { *reg |= 1u << k; }
// 读取第 k 个开关
bool isOn(uint32_t reg, int k) { return reg & (1u << k); }在嵌入式或游戏引擎中,此法节省大量缓存行。
判断 2 的幂次
bool powerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; }分析:唯一让 n & (n-1) == 0 的二进制特征即为单 1 后跟全 0。
进阶技巧:脑爆级算法黑科技
Kernighan 算法:统计 1 的数量
int popcount(unsigned int v) {
int cnt = 0;
while (v) { v &= v - 1; cnt++; }
return cnt;
}每次循环至少干掉最低位的 1,位运算优化后经久不衰。
快速取右端首 1
int lowbit(int v) { return v & -v; }常用于树状数组(Fenwick)维护前缀和。
无符号灰码转换
uint32_t toGray(uint32_t n) { return n ^ (n >> 1); }相邻值仅差一位,旋转编码器与磁盘控制器的最爱。
各主流语言速查表
| 语言 | 示例 | 备注 |
|---|---|---|
| C/C++ | a & b, a << 3 | 直接映射 CPU 指令 |
| Python | a ^ b | int 无限精度,注意负数补码 |
| Java | ~a | signed >>> unsigned 右移需 >>> |
| JavaScript | a >>> 0 | 隐式 32-bit 转换;>>> 无符号 |
性能 VS 可读性:理智选择
- 位运算在系统编程、图形渲染、高频交易中优势明显;
- 现代编译器已将简单的乘除/模运算自动优化成移位指令;
- 若代码逻辑因此晦涩,优先可读性+清晰注释,而非过度炫技。
常见坑与最佳实践
- 右移符号扩展:在 C 里对
-8 >> 1结果留心编译器实现差异。 - 移位溢出:STM32 上
1 << 31属 UB(Undefined Behavior)需特别处理。 - 括号优先级:
a & b == 0编译器解析为a & (b == 0),建议显式括号。
实践贴士:给掩码起宏或常量名,#define FLAG_READ 0x0001,更易维护。FAQ:那些第一眼懵的问题
Q1:为什么用位运算比乘法快?
现代 CPU 的移位指令只需一个时钟周期,而乘法器可能需要 3-5 周期,且占用更多晶体管资源。
Q2:Python int 无限大,用位运算还划算吗?
虽然数值无上限,但置换掩码或压缩布尔数组时依旧高效;大规模循环中可减少内存访问。
Q3:如何判断系统是大端还是小端?
借助 union 或指针强制转换读取首字节即可:
union { uint32_t i; uint8_t c[4]; } x = {0x01020304};
return x.c[0] == 0x04 ? 1 : 0;Q4:能否把浮点数也用位运算加速?
不能直接移位;但可把 float 的 IEEE-754 表示转成 uint32_t 再做 bit_cast,例如在 Unity Burst 里手写 SIMD。
Q5:为什么不能 x << -3?
C/C++ 标准规定移位量必须非负且小于位宽,否则行为未定义;建议封装安全函数。
Q6:位运算可取代所有加法和乘法吗?
理论上加法与乘法可以递归拆成位级操作,但会极度降低可读性;面试题除外,日常编码勿滥用。
结语
从最简单的奇偶判断,到 Fenwick 树与前缀和,再到加密算法中的 S-box 置换,位运算始终扮演着 CPU 拥抱二极管的初恋角色。掌握它们,你就获得了与「电子产品最底层语言」对话的钥匙。👉 现在就亲自动手测试这些魔法,看看能带来多少性能惊喜。