C++左移 << 和右移 >>
困难19C++ 中的左移 << 和右移 >>:让数字“跑位”的魔法
你有没有想过,计算机里的数字其实是一串“0”和“1”排成的队伍?左移(<<)和右移(>>)就像是给这支队伍下命令:“全体向左(或向右)跨一步!”结果数字就会神奇地变大或变小。掌握了它们,你可以快速计算乘法和除法,还能高效地处理一些编程技巧。
什么是位运算?为什么要学移位?
计算机中所有数据最终都以二进制(0和1)形式存储。位运算就是直接对二进制位进行操作,速度很快。左移和右移是其中最常见的两种,它们能把一个数的二进制位整体向左或向右移动,空位补0,移出去的位就丢掉。
左移 <<:向左跑,数字变大
规则:每左移一位,数值乘以2。左移n位,相当于乘以2的n次方。
为什么?因为二进制里,每向左移一位,每位数字的“权重”就翻倍(如个位变十位,十位变百位)。就像你在百位数后面加个0变成千位数,数字暴涨。
例子:小明的零花钱
小明每周有3元零花钱(二进制是 11,就是3)。如果他左移2位,相当于对3乘以2的2次方(4),得到12元。太棒了,零花钱翻了4倍!
#include <iostream>
using namespace std;
int main() {
int money = 3; // 原始零花钱:3元
int double_money = money << 1; // 左移1位,3×2=6元
int quadruple_money = money << 2; // 左移2位,3×4=12元
cout << "money: " << money << endl; // 3
cout << "money << 1: " << double_money << endl; // 6
cout << "money << 2: " << quadruple_money << endl; // 12
return 0;
}
更多练习
5 << 1:5的二进制是101,左移1位得1010,即10。5×2=10 ✅7 << 3:7×8=56,二进制111左移3位变成111000(56)✅- 注意:左移太多可能超过int能表示的范围,产生溢出,后面会讲。
右移 >>:向右跑,数字变小
规则:每右移一位,数值除以2(向下取整)。右移n位,相当于除以2的n次方。
注意:这里讲的右移是“逻辑右移”(对正数),简单理解就是去掉末尾的几位数字,数值自然变小。
例子:分糖果
班上有16颗糖(二进制 10000),要平均分给8个同学,每人得几颗?16÷8=2。右移3位就能搞定(因为2³=8)。
#include <iostream>
using namespace std;
int main() {
int candies = 16; // 糖果总数:16颗
int each = candies >> 3; // 右移3位,16÷8=2颗
cout << "candies >> 3: " << each << endl; // 2
return 0;
}
小数去哪儿了?
右移会丢弃低位(末尾的二进制位),所以结果总是整数,相当于向下取整。比如 9 >> 1:9的二进制 1001,右移1位得 100(4),9÷2=4.5,去掉小数部分得4。这跟C++整数除法/一样。
关于负数的右移
作为初学者,先只对正数做右移。负数右移在不同编译器下行为可能不同(算术右移会保留符号位),等以后学到再了解。我们目前安全地用unsigned int(无符号整数)也可以。
新手容易犯的错误
错误1:以为左移后原变量也会改变
int a = 5;
a << 1; // 只生成了一个临时值,a仍然等于5
cout << a; // 输出5,不是10!
正确做法:要么把结果赋给一个新变量,要么直接赋值回自身:
int b = a << 1; // 正确
a = a << 1; // 也正确,此时a变成10
错误2:左移太大造成溢出
int类型通常有32位,左移31位可能没问题,但左移32位或更多,就会把1移出所有位,结果变成0(或未定义行为)。例如:
int big = 1 << 31; // 可能变成负数(最高位是符号位)
int huge = 1 << 32; // 结果未定义
建议:左移位数不要超过数字的二进制位数减1。
错误3:右移负数
如果你对负数用右移,结果可能不是你想象的正数除以2。比如:
int neg = -16;
cout << (neg >> 2); // 不同编译器结果不同!可能是-4,也可能是某个大数
安全做法:只用正数或无符号整数做右移。
错误4:混淆优先级
移位运算符<<和>>的优先级比加减法低,但比关系运算符高。常见坑:
cout << 5 + 3 << 2; // 实际是 ( (5+3) << 2 ) = 8<<2 = 32
// 你可能想写的是 cout << (5+3<<2); // 还是32?注意优先级:<<从左到右,而+优先级高
// 如果想先移位再加,要加括号: cout << (5 << 2) + 3; // 20+3=23
规则:拿不准时,一律加括号。
完整可运行示例:零花钱与糖果大作战
下面是一个综合例子,演示左移和右移的实际效果,并比较与普通乘除的结果。
#include <iostream>
using namespace std;
int main() {
// ---------- 左移:零花钱 ----------
int pocket_money = 3; // 初始零花钱:3元
int after_left = pocket_money << 2; // 左移2位:3 * 4 = 12元
cout << "原始零花钱:" << pocket_money << "元" << endl;
cout << "左移2位后:" << after_left << "元" << endl;
// 验证:3 * 2^2 = 12
int multiply_test = pocket_money * 4; // 普通乘法
cout << "普通乘法验证:" << multiply_test << "元" << endl;
cout << "是否相等?" << (after_left == multiply_test ? "是" : "否") << endl;
cout << "------------------------" << endl;
// ---------- 右移:分糖果 ----------
int candy_total = 16; // 总糖果数:16颗
int each_student = candy_total >> 3; // 右移3位:16 / 8 = 2颗/人
cout << "总糖果:" << candy_total << "颗" << endl;
cout << "右移3位后每人得:" << each_student << "颗" << endl;
// 验证:16 / 8 = 2
int divide_test = candy_total / 8; // 普通除法(会截断小数)
cout << "普通除法验证:" << divide_test << "颗" << endl;
cout << "是否相等?" << (each_student == divide_test ? "是" : "否") << endl;
cout << "------------------------" << endl;
// ---------- 右移丢弃小数 ----------
int odd = 9; // 9元,分给2个人
int each_odd = odd >> 1; // 右移1位:9 / 2 = 4,丢掉0.5
cout << "9元右移1位:" << each_odd << "元 (向下取整)" << endl;
cout << "9元普通除法:" << (odd / 2) << "元" << endl;
// ---------- 常见错误演示 ----------
int a = 5;
a << 1; // 错误:没有保存结果!
cout << "错误示范后 a 依然是:" << a << endl; // 5
int b = 5;
b = b << 1; // 正确:赋值回自身
cout << "正确赋值后 b = " << b << endl; // 10
return 0;
}
生活小比喻与记忆口诀
- 左移:像在排队时每人向左跨一步,前面空位填0,后面的人掉出了队伍(丢弃)。数值变大,因为位置更靠前(高位)。
- 右移:像在排队时每人向右跨一步,前面的人掉出队伍(丢弃),后面空位填0。数值变小,因为位置更靠后(低位)。
口诀:向左跑,乘2快;向右跑,除2急;丢了小数不心疼,整数结果合心意。
相关指引
学完左移和右移,你可以继续探索其他位运算:
- 按位与 &:可以用来判断某个二进制位是否为1(如
num & (1 << k)提取第k位)。 - 按位或 |:可以把某些位置为1(如
num | (1 << k)设置第k位)。 - 按位异或 ^:可以交换两个变量(
a ^= b; b ^= a; a ^= b;),或者翻转特定位。 - 按位取反 ~:把0变1,1变0。
如果你想深入了解整数在计算机中的存储(原码、反码、补码),以及负数右移的具体行为,推荐先掌握补码知识。掌握了这些,位运算就能成为你写高效代码的利器!
例题精讲
已知 unsigned int a = 5; 执行 a = a << 3; 后,a 的值是多少?
int x = 23; int y = x >> 2; 则 y 的值是?
对于 unsigned int 类型的变量 a,表达式 a << 1 的结果等价于 a * 2。
执行以下代码后,输出结果是:
int x = 14;
int y = x >> 1;
cout << y;补全函数,实现用移位运算将整数 x 乘以 8:
int multiply_by_8(int x) {
return ___;
}