关于<<和>>操作的注意事项
在 C++ 中,<<
(左移)和 >>
(右移)是 位运算 操作符,用于对整数的二进制位进行移动。它们的补位规则(填充移出的空位)取决于 操作数的类型(是否有符号)和具体的实现(部分情况由编译器决定)。以下是详细分析:
1. 左移操作 <<
规则:低位补 0
- 无论操作数是 有符号(
signed
) 还是 无符号(unsigned
),左移操作<<
都会在 右侧(低位)补 0。 - 左移
n
位相当于乘以 (2^n)(如果未溢出)。
示例
unsigned int a = 0b1101; // 13
a = a << 2; // 0b110100 → 52 (13 * 4)
0b1101
左移 2 位 →0b110100
(右侧补 0)。
注意事项
-
如果左移导致 符号位被修改(对于
signed
类型),结果是 未定义行为(UB):int b = 0x40000000; // 2^30 b = b << 2; // UB! 可能变成负数或溢出
2. 右移操作 >>
右移的补位规则取决于 操作数是否有符号:
(1) 无符号数(unsigned
):高位补 0
- 逻辑右移(Logical Right Shift),高位直接补 0。
- 右移
n
位相当于除以 (2^n)(向下取整)。
示例
unsigned int c = 0b1101; // 13
c = c >> 2; // 0b0011 → 3 (13 / 4)
0b1101
右移 2 位 →0b0011
(左侧补 0)。
(2) 有符号数(signed
):高位补符号位(算术右移)
- 算术右移(Arithmetic Right Shift),高位补 符号位的值(正数补 0,负数补 1)。
- 右移
n
位相当于 带符号的除法(向负无穷取整)。
示例(正数)
int d = 0b1101; // 13 (假设 int 是 4 位)
d = d >> 2; // 0b0011 → 3 (13 / 4)
0b1101
(13)右移 2 位 →0b0011
(3)。
示例(负数)
int e = -8; // 0b11111000 (假设 8-bit)
e = e >> 2; // 0b11111110 → -2 (-8 / 4)
0b11111000
(-8)右移 2 位 →0b11111110
(-2),高位补 1。
3. 关键总结
操作 | 无符号数(unsigned ) | 有符号数(signed ) |
---|---|---|
<< | 低位补 0 | 低位补 0(但符号位被修改时是 UB) |
>> | 高位补 0(逻辑右移) | 高位补符号位(算术右移) |
注意事项
- 有符号数的左移可能 UB:如果左移后符号位被改变(如
1 << 31
),行为未定义。 - 右移负数的结果依赖实现:C++ 标准规定
signed >>
是 算术右移,但早期某些机器可能采用逻辑右移(现代编译器基本都遵循算术右移)。 - 避免对负数进行位运算:除非明确需要,否则建议使用
unsigned
进行位操作。
代码示例
#include <iostream>
using namespace std;
int main() {
// 左移(补 0)
unsigned int u1 = 0b1101; // 13
u1 = u1 << 2; // 0b110100 → 52
cout << u1 << endl; // 52
// 无符号右移(补 0)
unsigned int u2 = 0b1101; // 13
u2 = u2 >> 2; // 0b0011 → 3
cout << u2 << endl; // 3
// 有符号右移(补符号位)
int s1 = -8; // 0b11111000 (假设 8-bit)
s1 = s1 >> 2; // 0b11111110 → -2
cout << s1 << endl; // -2
return 0;
}
4. 如何强制逻辑右移?
如果需要对 signed
数进行逻辑右移(高位补 0),可以 先转换为 unsigned
:
int x = -8;
unsigned int y = (unsigned int)x >> 2; // 逻辑右移
5. 总结
<<
总是补 0(但有符号左移可能 UB)。>>
:unsigned
→ 补 0(逻辑右移)。signed
→ 补符号位(算术右移)。
- 建议:位运算尽量用
unsigned
,避免未定义行为。