【GESP】C++三级考试大纲知识点梳理, (3)位运算
GESP C++三级官方考试大纲中,共有8条考点,本文针对C++(3)号知识点进行总结梳理。
(3)掌握位运算:与(&)、或(|)、非(~)、异或(^)、左移(«)、右移(»)的基本使用方法及原理。
位运算是对二进制数据的直接操作,通常用于高效的数值计算和操作,尤其在嵌入式系统和性能优化中非常有用。理解位运算时,掌握它们的二进制原理是非常重要的。以下是从二进制的角度,对各个位运算符的详细说明:
一、与(&)运算符
1. (&)运算规则
原理:按位与运算符对两个数字的每一位进行比较,只有当 两个操作数的相应位都为 1 时,结果的相应位才为 1,否则为 0。
- 0 & 0 = 0
- 0 & 1 = 0
- 1 & 0 = 0
- 1 & 1 = 1
示例:
1
2
3
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a & b; // 运算过程:0101 & 0011 = 0001
过程:
1
2
3
4
0101 (5)
& 0011 (3)
-------
0001 (结果:1)
- 第1位:0 & 0 = 0
- 第2位:1 & 0 = 0
- 第3位:0 & 1 = 0
- 第4位:1 & 1 = 1
2. (&)应用场景
掩码操作,清除或检查某些特定位。掩码操作常用于清除数值中的某些位,或检查某些特定的位是否为 1。例如,清除某些低位、检查某一位的状态。
示例:检查一个数是否为偶数(通过与操作检查最低位)
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using namespace std;
int main() {
int num = 5; // 二进制:0101
if (num & 1) {
cout << "奇数" << endl; // 结果是奇数
} else {
cout << "偶数" << endl;
}
return 0;
}
在这个例子中,num & 1
用来检查最低位。如果最低位是 1(即数值为奇数),结果为 true
,否则为 false
。
二、或(|)运算符
1. (|)运算规则
按位或运算符对两个数字的每一位进行比较,当 两个操作数的相应位有一个为 1 时,结果的相应位就为 1,否则为 0。
- 0 | 0 = 0
- 0 | 1 = 1
- 1 | 0 = 1
- 1 | 1 = 1
示例:
1
2
3
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a | b; // 运算过程:0101 | 0011 = 0111
过程:
1
2
3
4
0101 (5)
| 0011 (3)
-------
0111 (结果:7)
- 第1位:0 | 0 = 0
- 第2位:1 | 0 = 1
- 第3位:0 | 1 = 1
- 第4位:1 | 1 = 1
2. (|)应用场景
设置某些特定位为 1,|
操作符可以用于将特定的位设置为 1,常见于标志位的设置。
示例:设置第 3 位为 1
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include<bitset>
using namespace std;
int main() {
int num = 0b1010; // 二进制:1010
num = num | 0b0100 // 设置第 3 位为 1
cout << "结果: " << std::bitset<8>(num) << endl; // 输出结果:00001110
return 0;
}
输出
1
结果: 00001110
在这个例子中,我们使用 |
操作符和左移操作符将第 3 位设置为 1。1 << 3
产生一个只有第 3 位为 1 的掩码。
三、非(~)运算符
1. (~)运算规则
按位非(取反)运算符对数字的每一位进行反转,将 1 变为 0,0 变为 1。在二进制表示中,通常涉及到补码的概念,之前提到过,在计算机中整数通常是以补码表示的。
示例:
1
2
int a = 5; // 二进制:0101
int result = ~a; // 运算过程:~0101 = 1010
过程:
1
2
3
4
原数:
0101 (5)
取反:
1010 (-6,补码表示)
- 注意:
~a
的结果不仅是二进制位的逐位取反,还会涉及到补码转化,导致负数的表示。
2.(~)应用场景
清除某一特定的位(设置为0),~
操作符用于取反,可以结合 &
操作符来将特定位置设为0,其他位不变。
示例:第3位置为0
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include<bitset>
using namespace std;
int main() {
int num2 = 0b1110; // 二进制:1110
num2 = num2 & ~0b0100; // 清除第 3 位
cout << "结果: " << std::bitset<8>(num2) << endl; // 输出结果:00001010
return 0;
}
在这个例子中,我们先使用 1 << 3
将第 3 位移动到相应位置,再通过 ~
运算取反得到掩码,最后通过 &
运算清除该位。
四、异或(^)运算符
1. (^)运算规则
按位异或运算符对两个数字的每一位进行比较,只有当 两个操作数的相应位不相同 时,结果的相应位才为 1,否则为 0。
运算规则:
- 0 ^ 0 = 0
- 0 ^ 1 = 1
- 1 ^ 0 = 1
- 1 ^ 1 = 0
示例:
1
2
3
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
int result = a ^ b; // 运算过程:0101 ^ 0011 = 0110
过程:
1
2
3
4
5
6
7
8
9
10
0101 (5)
^ 0011 (3)
-------
0110 (结果:6)
```console
- 第1位:0 ^ 0 = 0
- 第2位:1 ^ 0 = 1
- 第3位:0 ^ 1 = 1
- 第4位:1 ^ 1 = 0
2. (^)应用场景
切换某一位(^
异或操作符):切换位的状态(从 0 到 1 或从 1 到 0)
^
异或运算符常用于切换某一位的值。如果该位是 0,异或后变成 1;如果该位是 1,异或后变成 0。
示例:切换第 2 位
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include<bitset>
using namespace std;
int main() {
int num3 = 0b1100; // 二进制:1100
num3 = num3 ^ 0b0010; // 切换第 2 位
cout << "切换结果: " << std::bitset<8>(num3) << endl; // 输出结果:00001110
int num4 = 0b1110; // 二进制:1110
num4 = num4 ^ 0b0010; // 切换第 2 位
cout << "切换结果: " << std::bitset<8>(num4) << endl; // 输出结果:00001100
}
输出
1
2
切换结果: 00001110
切换结果: 00001100
在这个例子中,我们使用 ^
来切换第 2 位的值。如果该位原来是 1,则变为 0,反之亦然。
实现加密算法(异或):简单加密算法(异或加密)
异或操作被广泛用于简单的加密和解密算法中,特别是在没有要求强加密的情况下,它具有对称性,即 A ^ B ^ B = A
。
示例:使用异或实现加密和解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
int main() {
int key = 0x5A; // 加密密钥
int data = 123; // 明文数据
// 加密:数据与密钥异或
int encrypted = data ^ key;
cout << "加密结果: " << encrypted << endl;
// 解密:加密结果与密钥异或
int decrypted = encrypted ^ key;
cout << "解密结果: " << decrypted << endl;
return 0;
}
输出
1
2
加密结果: 33
解密结果: 123
在这个例子中,data ^ key
实现了数据的加密,而 encrypted ^ key
则是解密过程。由于异或具有对称性,解密过程与加密过程相同。
五、左移(«)运算符
1. («)运算规则
左移运算符将数字的二进制位向 左移动指定的位数,右侧用零填充。每移动一位,相当于将数字乘以 2(移位数等于乘以 2 的幂)。
示例:
1
2
3
4
int a = 5; // 二进制:0101
int result2 = a << 1; // 运算过程:0101 << 1 = 1010
cout << bitset<4>(result2) << endl;
cout << result2 << endl;
输出
1
2
1010
10
过程:
1
2
3
0101 (5)
<< 1 位
1010 (结果:10)
- 左移 1 位,相当于 5 * 2 = 10。
扩展:
- 左移 2 位,相当于 5 * 4 = 20(即乘以 2 的平方)。
- 左移 3 位,相当于 5 * 8 = 40(即乘以 2 的立方)。
2.(«)应用场景
快速的乘法运算,左移运算符 <<
实现乘以 2 的幂。这些运算通常比乘法运算更高效,特别是在需要优化性能时。
六、右移(»)运算符
1. (»)运算规则
右移运算符将数字的二进制位向 右移动指定的位数。对于有符号整数,符号位(最高位)会填充到左侧;对于负数,补充的是 1(补码表示)。
示例:
1
2
3
4
5
int b = 5; // 二进制:0101
int result3 = b >> 1; // 运算过程:0101 >> 1 = 0010
cout << bitset<4>(b) << endl;
cout << bitset<4>(result3) << endl;
cout << result3 << endl;
输出
1
2
3
0101
0010
2
过程:
1
2
3
0101 (5)
>> 1 位
0010 (结果:2)
- 右移 1 位,相当于 5 / 2 = 2(向下取整)。
负数的右移:
负数右移操作涉及 补码表示 和 符号扩展 的概念。在计算机中,负数是通过补码表示的。右移时,符号位(最高位)会根据操作数的符号进行扩展。下面详细介绍负数右移的操作和示例。
例如,-5
的补码表示过程如下:
- 正数
5
的二进制表示是:0000000000000101
(假设为 16 位)。 - 对其取反:
1111111111111010
。 - 加 1 得到补码:
1111111111111011
,这就是-5
的补码表示。
有符号右移(»):右移时,符号位会扩展,即高位填充符号位(1 或 0)。对于负数,符号位扩展为 1。
示例:
1
2
3
4
5
int c = -5; // -5 的二进制是 1111111111111011
int result4 = c >> 1; // 右移 1 位
cout << bitset<16>(c) << endl;
cout << bitset<16>(result4) << endl;
cout << result4 << endl;
输出
1
2
3
1111111111111011
1111111111111101
-3
运算过程:
1
2
原数: 1111111111111011 (-5)
右移 1 位:1111111111111101
结果:
- 右移 1 位后,符号位(1)被扩展,得到的结果是
1111111111111101
,即-3
(补码表示)。 - 因此,
-5 >> 1
的结果是-3
。
右移 2 位(有符号右移 >>
)
1
2
int num = -5; // -5 的二进制是 1111111111111011
int result = num >> 2; // 右移 2 位
运算过程:
1
2
原数: 1111111111111011 (-5)
右移 2 位:1111111111111110
结果:
- 右移 2 位后,符号位(1)继续扩展,得到的结果是
1111111111111110
,即-2
(补码表示)。 - 因此,
-5 >> 2
的结果是-2
。
2.(»)应用场景
快速的除法运算,左移运算符 >>
实现除以 2 的幂。这些运算通常比除法运算更高效,特别是在需要优化性能时。
七、总结
- 与(&):两个相应位都为 1 时结果为 1,否则为 0。
- 或(|):两个相应位只要有一个为 1,结果就是 1。
- 非(~):逐位取反,将 1 变为 0,0 变为 1。
- 异或(^):两个相应位不同时结果为 1,相同为 0。
- 左移(«):每左移一位,等于乘以 2。
- 右移(»):每右移一位,等于除以 2。
这些操作都基于二进制运算,因此理解它们的原理对编程中高效处理数据非常有帮助。
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP各级别考纲要点、知识拓展和练习题目清单详见C++学习项目主页
“luogu-”系列题目已加入洛谷Java、C++初学团队,作业清单,可在线评测,团队名额有限,欢迎加入。
“bcqm-”系列题目可在编程启蒙题库进行在线评测。