左移对无符号数逻辑右移、有符号数算术右移;io中的是重载操作符,与位运算无关;移位优先级高于比较但低于加减。

左移 是乘 2 的幂,但只对无符号数安全
左移 本质是把二进制位整体向左移动,空出的低位补 0。对非负整数来说,<code>a 等价于 <code>a * (1 ,也就是乘以 <code>2^n。
但要注意:int 是有符号类型,C++ 标准规定:对有符号数左移导致溢出(比如 INT_MAX )是未定义行为(UB)。编译器可能生成任意结果,甚至优化掉整个分支。
- 安全做法:左移前确保操作数是无符号类型,例如用
unsigned int或显式转换(unsigned int)a - 移位位数不能超过类型总位数(如
int通常是 32 位,a 在 C++20 前是 UB;C++20 起要求为 0) - 移位数为负值(如
a )直接触发未定义行为,编译器通常报错或静默忽略
右移 >> 分逻辑右移和算术右移,取决于类型
右移 >> 向右移动位,高位补什么,由操作数类型决定:
- 对
unsigned类型,高位补 0(逻辑右移),等价于无符号除法:a >> n≈a / (1U - 对
signed类型(如int),C++ 标准允许实现选择补符号位(算术右移)或补 0(逻辑右移),但所有主流编译器(GCC、Clang、MSVC)都采用算术右移 —— 即高位补原符号位,保持负数仍为负数 - 因此
-8 >> 1通常是-4,而不是2147483644(补 0 的结果)
注意:右移位数同样不能为负,也不能 ≥ 类型宽度;否则行为未定义。
用 和 <code>>> 做 I/O 时完全不是位运算
这是初学者最常混淆的点:std::cout 中的 <code> 是重载的流插入操作符,和位左移毫无关系;同理 <code>std::cin >> x 的 >> 是提取操作符。
- 它们只是“借用了”位运算符的符号,语义完全不同
- 编译器靠参数类型区分:
int 触发位运算,<code>std::ostream & 触发流操作 - 别试图用
std::cout 来“调试位移”,括号外的 <code> 仍是流操作 —— 这没问题;但若写成 <code>std::cout ,就变成输出 <code>a再输出2,不是位移
移位和乘除混用时,编译器不一定优化,且语义不同
虽然 a 在数学上等于 <code>a * 2,但两者语义不同:移位是位级操作,乘法是算术操作。现代编译器(如 GCC -O2)通常能将简单乘 2 的幂优化为移位,但反过来不成立 —— 移位无法替代乘法的全部能力。
-
a * 3无法用单次移位表达,而a 永远只能是乘 2 的幂 - 对负数,
-5 * 2 == -10是确定的,但-5 是未定义行为 —— 别为了“看起来快”强行替换 - 在嵌入式或性能关键路径中,若需明确位操作(如寄存器配置、掩码生成),必须用移位;若只是做数值计算,优先用
*和/,更清晰、更安全
真正容易被忽略的是:移位操作符的优先级低于加减,但高于比较。写 a 实际是 <code>a ,而 <code>a + 1 是 <code>a + (1 —— 不加括号极易出错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











