C++位运算符共6个:&(按位与,清零)、|(按位或,置位)、^(按位异或,翻转/交换/判等)、~(按位取反)、(右移,除幂)。

位运算符有哪些,各自作用是什么
C++ 中的位运算符共 6 个,全部是二元操作符(&、|、^、、>>)或一元(~),作用对象是整数类型的**二进制位**,不关心符号位含义,直接对补码表示操作。
-
&:按位与,常用于清零某些位(如x & 0xFF取低 8 位) -
|:按位或,常用于置位(如flags | FLAG_DEBUG开启标志) -
^:按位异或,可用于翻转特定位(x ^ 0x01翻转最低位)、交换变量(a ^= b; b ^= a; a ^= b;)或检测相等(a ^ b == 0) -
~:按位取反,一元运算,注意结果类型与操作数一致(~(unsigned char)0是0xFF,不是0xFFFFFFFF) :左移,等价于乘以 2 的幂(x ≡x * (1 ),但仅对无符号或非负有符号数安全-
>>:右移,行为依赖类型:unsigned是逻辑右移(高位补 0),signed是算术右移(高位补符号位),C++ 标准未强制规定 signed 右移填充方式,但主流编译器(GCC/Clang/MSVC)均实现为算术右移
左移和右移的溢出与未定义行为风险
对有符号整数执行左移时,若结果超出该类型可表示范围(如 int x = 1 在 32 位 int 上),属于**未定义行为(UB)**。右移负数虽在实践中常见,但 C++20 前标准未明确定义其语义(C++20 起明确为算术右移),仍建议避免依赖。
- 永远不要对负数做左移(
-1 是 UB) - 避免对有符号数右移后参与算术计算,除非你确认编译器行为且已测试边界值(如
-5 >> 1在多数平台得-3,而非2147483645) - 更安全的做法:先转为对应宽度的
unsigned类型再移位,例如(unsigned int)x >> n - 移位位数不能为负,也不能 ≥ 操作数位宽(如
int x; x 在 32 位int上是 UB)
按位逻辑运算中常见的隐式类型提升陷阱
C++ 会对小整型(char、short)在位运算前进行**整型提升(integer promotion)**,升为 int(或 unsigned int),这会导致结果宽度意外变大,尤其在组合多个小类型操作时容易出错。
- 例如:
unsigned char a = 0xFF, b = 0x0F; auto c = a & b;→c类型是int,值为15,而非unsigned char - 若后续赋值给
unsigned char变量,会截断,但中间过程可能引发符号扩展问题(如char a = -1; a & 0x0F先提升为int(-1),即0xFFFFFFFF,再 &0x0F得15) - 解决方法:显式转换回目标类型,或使用
static_cast(a & b) - 用
std::byte(C++17)可规避部分歧义,但它不支持直接位运算,需先转为整型
位运算在实际工程中的典型误用点
位运算本身高效,但滥用或误解语义反而引入隐蔽 bug,尤其在跨平台、嵌入式或序列化场景中。
立即学习“C++免费学习笔记(深入)”;
- 用
|拼接两个字节构造uint16_t时忽略字节序:(hi 假设大端,实际可能是小端设备,应改用htons()或std::byteswap - 用
^实现“条件翻转”时未考虑初始状态,比如flag ^= ENABLED本意是切换,但如果ENABLED不是单一位掩码(如值为3),就会同时翻转多位,破坏其他标志 - 用
& ~MASK清零时,MASK定义错误(如#define MASK 0x0F00写成0xF00),导致清零范围偏移 - 在 constexpr 上下文中使用位运算时,确保所有操作数都是字面量或 constexpr 变量,否则编译失败(如
constexpr int x = y 中y非 constexpr)
位运算的“底层感”容易让人忽略类型、符号、提升、序这些细节——它们不出现在汇编指令里,却实实在在决定结果是否可移植、可复现。











