1.简介
C++中的位运算包括与(&)
、或(|)
、异或(^)
、非(~)
、左移(<<)
和右移(>>)
等,这些运算直接作用于整数的二进制表示。
- 按位与(Bitwise AND):对两个数的每一位进行与操作,只有两个对应的位都为1时结果才为1,否则为0。
- 按位或(Bitwise OR):对两个数的每一位进行或操作,只要有一个对应的位为1,结果就为1。
- 按位取反(Bitwise NOT):对一个数的每一位进行取反操作,0变为1,1变为0。
- 按位异或(Bitwise XOR):对两个数的每一位进行异或操作,当两个对应的位不同时结果为1,相同时结果为0。
- 左移(Left Shift):将一个数的所有位向左移动指定的位数,右边空出的位用0填充。
- 右移(Right Shift):将一个数的所有位向右移动指定的位数,对于无符号数,左边空出的位用0填充;对于有符号数,具体的行- 取决于编程语言和编译器,有的语言会用符号位(即最左边的位)来填充左边的空位。
2.与(&)
2.1 简介
与运算是C++中的一种基本位运算,它直接对两个整数的二进制表示进行操作。对于两个数的每一位,只有当两个数的对应位都为1时,结果的对应位才为1,否则为0。位与运算的符号是&。
2.2 示例
/*
* @brief: Bitwise operation
* @compile: g++ -g bm_main.cc -o d -std=c++11
* @author: your name
* @date: 2024/10/12
* @lastEditorDate:
*/
#include <iostream>
void printBinary(int num)
{
const int bit_count = 8; //为方便这里采用了8位
for (int i = bit_count - 1; i >= 0; --i)
{
if ((num >> i) & 1)
{
std::cout << '1';
}
else
{
std::cout << '0';
}
}
}
int main(int argc, char* argv[])
{
int a = 7;
int b = 6;
int r = a & b;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
std::cout << "a & b value is: : " << r << std::endl;
return 0;
}
输出
a value(bit) is: 00000111
b value(bit) is: 00000110
r value(bit) is: 00000110
a & b value is: : 6
在输出的结果中可以直观的看到,a和b的计算逻辑,当两个数的对应位都为1时,结果的对应位才为1,否则为0。为方便演示,这里只打印出8位。
2.3 应用
2.3.1 计算方面
- 清零特定位:通过将某个数与一个掩码(mask)进行位与运算,可以将该数的特定位清零。掩码是一个在特定位上为0,其他位上为1的整数。
- 检查特定位:通过与一个只在特定位上为1,其他位上为0的整数进行位与运算,可以检查一个数的特定位是否为1。如果结果为0,则该位为0;否则,该位为1。
- 提取特定位:通过与一个只在特定位上为1,其他位上为0的整数进行位与运算,并右移适当位数,可以提取一个数的特定位的值。
int main(int argc, char* argv[])
{
uint8_t binary_template = 0b00000010; //8位二进制模板的定义
int a = 7;
int r = a & binary_template; //保留右边第二位,其它位置清零
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
return 0;
}
a value(bit) is: 00000111
r value(bit) is: 00000010
2.3.2 图像处理
在图像处理领域,灰度图中的每一个像素的取值都是0-255,也就是8位的二进制数值。可以通过位与运算,可以提取、修改或合并图像中个灰度值。
在图像处理领域,彩色图像是由RGB 3个颜色通道组成,每一个颜色通道和灰度图一样,3个颜色通道组成了一张彩色图像。同样可以通过位与运算,可以提取、修改或合并图像中的颜色分量。
3.或(|)
3.1 简介
C++中的或运算是一种基本的逻辑运算方式,符号表示为“|”。它按照二进制位进行运算,当参与运算的两个二进制位中只要有一个为1时,所得结果的该位数字即为1;当两个二进制位都为0时,所得结果的该位数字才为0。这种运算方式在处理二进制数据时非常有用,特别是在需要进行数据位操作的场景中。
3.2 示例
/*
* @brief: Bitwise operation
* @compile: g++ -g bm_main.cc -o d -std=c++11
* @author: your name
* @date: 2024/10/12
* @lastEditorDate:
*/
#include <iostream>
void printBinary(int num)
{
const int bit_count = 8; //为方便这里采用了8位
for (int i = bit_count - 1; i >= 0; --i)
{
if ((num >> i) & 1)
{
std::cout << '1';
}
else
{
std::cout << '0';
}
}
}
int main(int argc, char* argv[])
{
int a = 7;
int b = 6;
int r = a | b;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
std::cout << "a & b value is: : " << r << std::endl;
return 0;
}
输出
a value(bit) is: 00000111
b value(bit) is: 00000110
r value(bit) is: 00000111
a & b value is: : 7
3.3 应用
应用领域主要包括特殊场景下的计算、图像处理和加密领域的优化算法等。
4.异或(^)
4.1 简介
C++中的异或运算是一种基本的位运算,符号为“^”。异或运算的规则是:当两个操作数的对应位不同时,结果为1;当两个操作数的对应位相同时,结果为0。这种运算方式在处理二进制数据时非常有用,特别是在需要进行数据位操作的场景中。
4.2 示例
/*
* @brief: Bitwise operation
* @compile: g++ -g bm_main.cc -o d -std=c++11
* @author: your name
* @date: 2024/10/12
* @lastEditorDate:
*/
#include <iostream>
void printBinary(int num)
{
const int bit_count = 8; //为方便这里采用了8位
for (int i = bit_count - 1; i >= 0; --i)
{
if ((num >> i) & 1)
{
std::cout << '1';
}
else
{
std::cout << '0';
}
}
}
int main(int argc, char* argv[])
{
int a = 10;
int b = 18;
int r = a ^ b;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
std::cout << "a & b value is: : " << r << std::endl;
return 0;
}
输出
a value(bit) is: 00001010
b value(bit) is: 00010010
r value(bit) is: 00011000
a & b value is: : 24
在输出的结果中可以看出,当两个操作数的对应位不同时,结果为1;当两个操作数的对应位相同时,结果为0,相同的情况包括全部为0,或者全部为1。
4.3 应用
4.3.1 交互两个变量的值
计算的关键
//a和b交互数值
a = a ^ b;
b = a ^ b;
a = a ^ b;
int main(int argc, char* argv[])
{
int a = 10;
int b = 18;
std::cout << "raw data" << std::endl;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl << std::endl;
std::cout << "calculation process" << std::endl;
a = a ^ b; // a现在为a与b的异或结果
std::cout << "a[a ^ b] value(bit) is: "; printBinary(a); std::cout << std::endl;
b = a ^ b; // b现在为a的原始值(因为a现在是a^b,所以a^b^b=a)
std::cout << "b[a ^ b] value(bit) is: "; printBinary(b); std::cout << std::endl;
a = a ^ b; // a现在为b的原始值(因为b现在是a,所以a^a^b=b)
std::cout << "a[a ^ b] value(bit) is: "; printBinary(a); std::cout << std::endl<<std::endl;
std::cout << "result" << std::endl;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl << std::endl;
std::cout << "a value is: : " << a << std::endl;
std::cout << "b value is: : " << b << std::endl;
return 0;
}
输出
raw data
a value(bit) is: 00001010
b value(bit) is: 00010010
calculation process
a[a ^ b] value(bit) is: 00011000
b[a ^ b] value(bit) is: 00001010
a[a ^ b] value(bit) is: 00010010
result
a value(bit) is: 00010010
b value(bit) is: 00001010
a value is: : 18
b value is: : 10
4.3.2 检测奇偶数
关键:
异或运算还可以用于检测一个整数是奇数还是偶数。由于偶数的二进制表示中最低位(即最右边的位)总是0,而奇数的最低位总是1,因此可以通过将该整数与1进行异或运算,然后判断结果是否为0来判断该整数是奇数还是偶数。如果结果为0,则该整数为偶数;如果结果不为0,则该整数为奇数。
int main(int argc, char* argv[])
{
int a = 11;
int b = 18;
std::cout << "raw data" << std::endl;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
int r = a ^ 1;
std::cout << "1 value(bit) is: "; printBinary(1); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
if ((a ^ 1) == 0)
{
std::cout << a << " is even." << std::endl;
}
else {
std::cout << a << " is odd." << std::endl;
}
std::cout << std::endl;
std::cout << "raw data" << std::endl;
std::cout << "b value(bit) is: "; printBinary(b); std::cout << std::endl;
r = b ^ 1;
std::cout << "1 value(bit) is: "; printBinary(1); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl;
if ((b ^ 1) == 0)
{
std::cout << b << " is even." << std::endl;
}
else {
std::cout << b << " is odd." << std::endl;
}
return 0;
}
输出
raw data
a value(bit) is: 00001011
1 value(bit) is: 00000001
r value(bit) is: 00001010
11 is odd.
raw data
b value(bit) is: 00010010
1 value(bit) is: 00000001
r value(bit) is: 00010011
18 is odd.
5.非(~)
5.1 简介
位运算非用符号“~”表示,用于对一个整型或字符型数据进行按位取反操作。当应用于整数时,按位取反运算符会将整数的二进制表示中的每一位取反,即将0变为1,将1变为0。
这里只介绍正数的按位取反结果,因为负数的按位取反结果涉及到补码表示和整数的存储方式,比较复杂。
5.2 示例
int main(int argc, char* argv[])
{
int a = 10;
int r = ~a;
std::cout << "a value(bit) is: "; printBinary(a); std::cout << std::endl;
std::cout << "r value(bit) is: "; printBinary(r); std::cout << std::endl << std::endl;
std::cout << "a value is: : " << a << std::endl;
std::cout << "r value is: : " << r << std::endl;
return 0;
}
输出
a value(bit) is: 00001010
r value(bit) is: 11110101
a value is: : 10
r value is: : -11
这里的取反为什么是-11???
这里先说两个概念,1.是有符号和无符号的整数表示;2.是负数的表示方法
I. 有符合和无符号 以8位为例,有符合其表示的数值范围为:0-255;无符号其表示的数值范围为:-128-127;对于有符号而言,通常使用最高位(最左边的位)作为符号位,0表示正数,1表示负数。剩余的7位用于表示数值的大小。
正数的范围是从0到 2^7 −1(因为符号位为0,所以只有7位用于表示数值):
- 最小值:0
- 最大值:127 负数的范围是从 −2^7 到 -1(因为符号位为1,所以7位表示的是正数,但整体表示负数):
- 最小值:-128
- 最大值:-1 所以,8位有符号二进制数的表示范围是:-128到127。
II.负数的表示 在计算机科学中,负数通常使用二进制补码来表示。补码表示法是一种用于表示有符号整数的二进制编码方式,它允许使用相同的位数来表示正数和负数,并且使得加法、减法和乘法等运算在二进制层面上的实现更为简单和统一。
以下是负数使用二进制补码表示的步骤:
- 确定整数的位数:首先,需要确定用来表示整数的二进制位数,比如8位、16位、32位或64位等。这个位数决定了能够表示的最大正数和最小负数。
- 找到正数的二进制表示:对于要表示的负数,先找到其绝对值的二进制表示。例如,如果要表示-5,先找到5的二进制表示。
- 按位取反:将正数的二进制表示的每一位都翻转(即将0变为1,将1变为0)。这一步相当于对该数进行了一次按位取反操作。
- 加1:在按位取反的结果上加1。这一步是将上一步得到的反码转换为补码。
- 结果:得到的结果就是该负数在补码表示法下的二进制编码。
例如,用8位二进制数来表示-5:
5的二进制表示是 00000101。
按位取反后得到 11111010。
在 11111010 上加1,得到 11111011。
因此,-5在8位二进制补码表示法下是 11111011。
int main(int argc, char* argv[])
{
int a1 = 0;
int a2 = 1;
int a3 = 127;
int a4 = -1;
int a5 = -127;
int a6 = -128;
std::cout << "a1[ 0] value(bit) is: "; printBinary(a1); std::cout << std::endl;
std::cout << "a2[ 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
std::cout << "a3[ 127] value(bit) is: "; printBinary(a3); std::cout << std::endl;
std::cout << "a4[ -1] value(bit) is: "; printBinary(a4); std::cout << std::endl;
std::cout << "a5[-127] value(bit) is: "; printBinary(a5); std::cout << std::endl;
std::cout << "a6[-128] value(bit) is: "; printBinary(a6); std::cout << std::endl;
return 0;
}
输出
a1[ 0] value(bit) is: 00000000
a2[ 1] value(bit) is: 00000001
a3[ 127] value(bit) is: 01111111
a4[ -1] value(bit) is: 11111111
a5[-127] value(bit) is: 10000001
a6[-128] value(bit) is: 10000000
-127 是127[01111111]取反等于 10000000,再加1,等于10000001;
-128 是128 [10000000] 取反等于 01111111,再加1,等于10000000;
这里解释为什么10取反之后是-11 10的二进制为 00001010 取反之后位 11110101
根据负数的表示方法,11110101减1等于11110100,再取反等于
00001011,00001011表示的是11,所以10取反之后是-11
5.3 应用
按位取反运算符常用于位运算中,如掩码操作、位翻转等。此外,在某些算法中,按位取反运算符也可以用于实现特定的逻辑功能。
6.左移(<<)
6.1 简介【用于无符号数据】
左移位运算是C++中的一种位运算操作符,符号为 <<。它主要用于将一个数的二进制表示向左移动指定的位数,右边空出的位用0填充。左移位运算通常用于快速乘以2的幂次(即乘以2、4、8、16等)。
左移位运算的操作方式是将一个整数的二进制位向左移动若干位,右边空出的位用0填充。例如,如果你有一个整数 x,并且你将其左移 n 位,其效果等同于 x * 2^n(在不考虑溢出的情况下)。
6.2 示例
int main(int argc, char* argv[])
{
int a1 = 9;
int a2 = a1 << 1;
int a3 = a1 << 2;
int a4 = a1 << 3;
std::cout << "a1: " << a1 << std::endl;
std::cout << "a2: " << a2 << std::endl;
std::cout << "a3: " << a3 << std::endl;
std::cout << "a4: " << a4 << std::endl;
std::cout << "a1[ ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
return 0;
}
输出
a1: 9
a2: 18
a3: 36
a4: 72
a1[ ] value(bit) is: 00001001
a2[a1 << 1] value(bit) is: 00010010
a3[a1 << 2] value(bit) is: 00100100
a4[a1 << 3] value(bit) is: 01001000
6.3 应用
- 快速乘法:左移位运算可以用来快速计算2的幂次。例如,x << 3 相当于 x * 2^3,即 x * 8。
- 位掩码操作:在处理位掩码时,左移位运算可以用来设置或清除特定位。
- 算法优化:在某些算法中,左移位运算可以用来代替乘法运算,从而提高性能。
注意:
- 左移位操作可能会导致数据溢出,特别是当移位后的结果超过了数据类型的最大范围时。
- 对于有符号整数,左移位操作的行为是未定义的(对于超出数据类型的位数进行左移)。因此,通常建议对无符号整数进行左移位操作。
7.右移(>>)
7.1 简介
右移位运算是C++中的另一种位运算操作符,符号为 >>。它主要用于将一个数的二进制表示向右移动指定的位数,左边空出的位根据数的符号进行填充:对于无符号数,左边空出的位用0填充;对于有符号数,左边空出的位通常用符号位(即最左边的位,0表示正数,1表示负数)的值来填充,这称为算术右移。
右移位运算的操作方式是将一个整数的二进制位向右移动若干位,左边空出的位根据数的类型进行填充。例如,如果你有一个整数 x,并且你将其右移 n 位,其效果等同于 x / 2^n(在不考虑舍入和溢出的情况下,且对于无符号数或算术右移的有符号数)。
7.2 示例
无符号
int main(int argc, char* argv[])
{
int a1 = 9;
int a2 = a1 >> 1;
int a3 = a1 >> 2;
int a4 = a1 >> 3;
int a5 = a1 >> 4;
int a6 = a1 >> 5;
std::cout << "a1: " << a1 << std::endl;
std::cout << "a2: " << a2 << std::endl;
std::cout << "a3: " << a3 << std::endl;
std::cout << "a4: " << a4 << std::endl;
std::cout << "a5: " << a5 << std::endl;
std::cout << "a6: " << a6 << std::endl;
std::cout << "a1[ ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
std::cout << "a5[a1 << 4] value(bit) is: "; printBinary(a5); std::cout << std::endl;
std::cout << "a6[a1 << 5] value(bit) is: "; printBinary(a6); std::cout << std::endl;
return 0;
}
输出
a1: 9
a2: 4
a3: 2
a4: 1
a5: 0
a6: 0
a1[ ] value(bit) is: 00001001
a2[a1 << 1] value(bit) is: 00000100
a3[a1 << 2] value(bit) is: 00000010
a4[a1 << 3] value(bit) is: 00000001
a5[a1 << 4] value(bit) is: 00000000
a6[a1 << 5] value(bit) is: 00000000
有符号
int main(int argc, char* argv[])
{
int a1 = -9;
int a2 = a1 >> 1;
int a3 = a1 >> 2;
int a4 = a1 >> 3;
int a5 = a1 >> 4;
int a6 = a1 >> 5;
std::cout << "a1: " << a1 << std::endl;
std::cout << "a2: " << a2 << std::endl;
std::cout << "a3: " << a3 << std::endl;
std::cout << "a4: " << a4 << std::endl;
std::cout << "a5: " << a5 << std::endl;
std::cout << "a6: " << a6 << std::endl;
std::cout << "a1[ ] value(bit) is: "; printBinary(a1); std::cout << std::endl;
std::cout << "a2[a1 << 1] value(bit) is: "; printBinary(a2); std::cout << std::endl;
std::cout << "a3[a1 << 2] value(bit) is: "; printBinary(a3); std::cout << std::endl;
std::cout << "a4[a1 << 3] value(bit) is: "; printBinary(a4); std::cout << std::endl;
std::cout << "a5[a1 << 4] value(bit) is: "; printBinary(a5); std::cout << std::endl;
std::cout << "a6[a1 << 5] value(bit) is: "; printBinary(a6); std::cout << std::endl;
return 0;
}
输出
a1: -9
a2: -5
a3: -3
a4: -2
a5: -1
a6: -1
a1[ ] value(bit) is: 11110111
a2[a1 << 1] value(bit) is: 11111011
a3[a1 << 2] value(bit) is: 11111101
a4[a1 << 3] value(bit) is: 11111110
a5[a1 << 4] value(bit) is: 11111111
a6[a1 << 5] value(bit) is: 11111111
7.3 应用
- 快速除法:右移位运算可以用来快速计算除以2的幂次。例如,x >> 3 相当于 x / 2^3,即 x / 8(对于无符号数或算术右移的有符号数)。
- 位掩码操作:在处理位掩码时,右移位运算可以用来右移特定位,从而进行特定的位操作。
- 算法优化:在某些算法中,右移位运算可以用来代替除法运算,从而提高性能。
注意
- 对于有符号整数,右移位操作可能是算术右移(用符号位填充)或逻辑右移(用0填充),这取决于编译器和平台。在C++标准中,对于有符号数的右移位操作,算术右移是首选行为,但逻辑右移也是允许的。因此,为了编写可移植的代码,最好避免对有符号数进行右移位操作,除非你确定平台的行为。
- 右移位操作也可能导致数据溢出或符号扩展问题,特别是当移位后的结果超出了数据类型的范围时。
- 对于无符号整数,右移位操作总是用0填充左边空出的位。