位运算仅在2的幂次下安全替代算术操作,负数右移存在符号扩展陷阱,交换变量应避免异或技巧,标志位操作需语义清晰,跨平台位操作应使用无符号类型。

用 & 和 | 替代取模和乘除,但只在 2 的幂次下安全
对 2 的幂次(如 4、8、16)做取模或整除时,x & (n-1) 确实比 x % n 快,x >> k 也比 x / (1 更直接。但这不是万能优化——一旦 <code>n 不是 2 的幂,& 就完全等价于错误结果。
- 正确场景:
addr & 0x7等价于addr % 8,前提是addr非负;负数用&会得到意外的大正数 - 编译器通常已自动优化
% 8为位运算,手动替换未必提升性能,反而降低可读性 -
x >> 2对负数执行的是算术右移(符号位填充),而x / 4是向零取整,两者在负数时结果不同
^ 交换变量值看似巧妙,实际不推荐
写 a ^= b; b ^= a; a ^= b; 能在不引入临时变量的情况下交换两个整型变量,但现代 CPU 流水线和编译器优化让这种“技巧”既无必要又容易出错。
- 若
a和b指向同一内存地址(比如swap(&x, &x)),结果是a变成 0,彻底丢失原值 - 编译器对普通三行赋值(含临时变量)早已生成最优汇编,甚至可能消除临时变量本身
- 该写法不适用于浮点、指针、类对象等类型,C++ 中更应依赖
std::swap
检查标志位用 &,设置/清除用 | 和 &~,别混用 ^
位字段常用于状态管理,比如权限掩码或配置选项。关键在于操作符语义清晰:检测是否置位是“与”,开启某位是“或”,关闭某位是“与非”。
- 检测:
if (flags & FLAG_READ)—— 正确;if (flags ^ FLAG_READ)是错的,它判断是否“不同”,不是“包含” - 设位:
flags |= FLAG_WRITE,清晰且可重复执行 - 清位:
flags &= ~FLAG_DEBUG,注意~作用于常量,不是变量;写成flags &= !FLAG_DEBUG是逻辑非,完全无效 - 翻转位(toggle)才用
^:flags ^= FLAG_VERBOSE
右移符号扩展陷阱:>> 在有符号数上行为依赖实现
C++ 标准只要求右移有符号数时,高位补符号位(算术右移)或补 0(逻辑右移)之一,具体由编译器和目标平台决定。这意味着代码可能在 x86 上正常,在 ARM 或某些嵌入式平台出错。
立即学习“C++免费学习笔记(深入)”;
- 最稳妥做法:对需要逻辑右移的场景,先转为无符号类型:
(unsigned int)x >> 3 - 不要假设
int8_t(-1) >> 1一定是 -1;它可能是 -1(算术)或 127(逻辑),取决于底层 - 使用
std::bitset或uint32_t等明确无符号类型处理位操作,能规避大部分歧义
位运算真正的价值不在“炫技式优化”,而在精确控制二进制布局、对接硬件协议、实现高效哈希或压缩算法。一旦离开这些明确场景,盲目替换算术操作,往往换来的是可维护性下降和隐蔽的跨平台 bug。










