C++按位与 & 和按位或 |
困难26开关与灯泡:轻松学会C++按位与(&)和按位或(|)
在日常编程中,我们经常需要直接操作二进制位,就像控制一排灯泡的亮灭一样。位运算就是计算机在二进制(0和1)层面上的操作。二进制只有两种状态:0表示“灭”,1表示“亮”。按位与和按位或就像两个开关同时控制一个灯泡,非常直观。掌握它们后,你就能高效地处理权限、标志位、数据压缩等问题。
按位与 &
规则:只有两个对应的位都是1时,结果位才是1;否则为0。
这就像两个串联的开关:必须同时闭合(1),灯泡才亮(1)。任何一个开关断开(0),灯泡就灭(0)。
例子:
int a = 6; // 二进制 110
int b = 3; // 二进制 011
int c = a & b; // 110 & 011 = 010 => 2
逐位比较:
- 第0位:0 & 1 = 0
- 第1位:1 & 1 = 1
- 第2位:1 & 0 = 0
结果是二进制010,等于十进制 2。
更多例子:
- 检查一个数的某一位是否为1。比如要判断数字
n的第3位(从右数,第0位开始)是不是1:
int result = n & (1 << 3);如果结果不等于0,说明该位为1。 - 清零某一位:
n = n & ~(1 << 2);将第2位强制变成0。
按位或 |
规则:只要两个对应位中有一个是1,结果位就是1;两个都是0时结果才为0。
这就像两个并联的开关:任意一个闭合(1),灯泡就亮(1)。只有两个都断开(0),灯泡才灭(0)。
例子:
int a = 6; // 110
int b = 3; // 011
int d = a | b; // 110 | 011 = 111 => 7
逐位比较:
- 第0位:0 | 1 = 1
- 第1位:1 | 1 = 1
- 第2位:1 | 0 = 1
结果是111,等于十进制 7。
更多例子:
- 设置某一位为1:
n = n | (1 << 4);将第4位强制变成1。 - 合并多个标志位:比如用整数表示多个开关状态,
flags = flags | OPTION_A | OPTION_B;。
生活小比喻
按位与:想象你的书包有两个扣子。只有两个都扣上,书包才安全(结果=1)。只要有一个没扣,书包就可能掉东西(结果=0)。
按位或:想象你的房门有无钥匙智能锁和机械锁。任意一个锁打开,门就能开(结果=1)。只有两个锁都锁着,门才打不开(结果=0)。
考试例子:
- 按位与:要参加夏令营,既需要语文成绩 ≥ 90(1),又需要数学成绩 ≥ 90(1)。只有两门都达标才算通过(1)。
- 按位或:周末去游乐园,可以玩过山车或者海盗船。只要至少玩一个项目,今天就算开心(1);两个都不玩才不开心(0)。
常见错误
-
混淆
&和&&&&是逻辑与,用于布尔值(true/false),结果是 true 或 false。&是按位与,对整数的每一位独立运算,结果是整数。- 错误代码:
if (a & b)本意可能是逻辑判断,但实际按位与后可能得到一个非零整数(被当作true),可能导致逻辑混乱。 - 正确做法:判断某位存在时用
if ((a & b) != 0),或者用if ((a & b))(非零即true)但最好显式比较。
-
优先级问题
运算符优先级中,位运算符&和|比关系运算符(如==)低,但比赋值运算符高。容易出错的例子:if (x & y == 0) // 实际被解释为 if (x & (y == 0)),因为 == 优先级高于 &正确写法:
if ((x & y) == 0) -
忘记对负数处理
负数的二进制表示是补码,按位与/或的结果可能出乎意料。例如-1的二进制全是1,-1 & 3 = 3。初学者应先用无符号整数或正整数练习。 -
用
&判断奇偶比取模更快但容易误解
if (n & 1)判断奇数(最低位为1),但写成if (n % 2 == 1)更直观。位运算适合性能敏感场景,但可读性稍差。
完整示例:判断奇偶、设置标志位
下面的程序展示了按位与和按位或的实用场景:
#include <iostream>
using namespace std;
int main() {
// 1. 判断奇偶(按位与)
int number = 9; // 二进制 1001
if (number & 1) { // 检查最低位:1为奇数,0为偶数
cout << number << " 是奇数" << endl; // 输出:9 是奇数
} else {
cout << number << " 是偶数" << endl;
}
// 2. 设置第3位为1(按位或)
int flags = 0; // 二进制 0000
int mask = 1 << 3; // 二进制 1000 (十进制8)
flags = flags | mask; // 现在 flags = 8 (1000)
cout << "设置第3位后 flags = " << flags << endl;
// 3. 检查第1位是否被设置(按位与)
int checkMask = 1 << 1; // 二进制 0010 (十进制2)
if (flags & checkMask) {
cout << "第1位是1" << endl;
} else {
cout << "第1位是0" << endl; // 输出:第1位是0
}
// 4. 按位与和或的直观演示
int a = 6; // 110
int b = 3; // 011
cout << "a & b = " << (a & b) << endl; // 2 (010)
cout << "a | b = " << (a | b) << endl; // 7 (111)
return 0;
}
运行结果:
9 是奇数
设置第3位后 flags = 8
第1位是0
a & b = 2
a | b = 7
总结与扩展
- 按位与:
&,用于清除某些位、检查位状态、乘法快速取模(如n & 7相当于n % 8)。 - 按位或:
|,用于设置某些位、合并标志位。
你已经学会了位运算的两个基础操作。接下来可以继续探索:
- 按位异或
^:相同为0,不同为1——用于交换变量、加密。 - 按位取反
~:0变1,1变0——用于清除位。 - 左移
<<和右移>>:快速乘以2或除以2。
把位运算想象成用开关控制灯泡,很快就能得心应手。试着用它们解决实际问题吧!
例题精讲
已知 int a = 0b1100, b = 0b1010; 则表达式 a & b 的值是(二进制形式)?
按位或运算中,两个操作数的对应位只要有一个为1,结果位就是1。
以下代码将变量x的第3位(从0开始)设置为1,请补全运算符号:
int x = 10; // 二进制 00001010
x = x ___ (1 << 3); // 期望x变为 00010010下列哪个表达式可以清空变量y的低4位(即将低4位设为0,其他位不变)?
请补全以下代码,判断变量num的第5位(从0开始)是否为1:
int num = 42;
if ( (num ___ (1 << 5)) != 0 )
cout << "第5位为1";