C++运算符:位运算 与 bitset类库
一、C++位运算
1. 位运算操作符
按位与(&)
功能:对两个操作数的每一位进行逻辑与操作。只有当两个相应位都为1时,结果位才为1。
int a = 5; // 二进制为0101 int b = 3; // 二进制为0011 int c = a & b; // 结果为0001,即1
按位或(|)
功能:对两个操作数的每一位进行逻辑或操作。只要两个相应位中有一个为1,结果位就为1。
int a = 5; int b = 3; int c = a | b; // 结果为0111,即7
按位异或(^)
功能:对两个操作数的每一位进行异或操作。当两个相应位不同时,结果位为1;相同时,结果位为0。
int a = 5; int b = 3; int c = a ^ b; // 结果为0110,即6
按位取反(~)
功能:对操作数的每一位进行取反操作,0变为1,1变为0。
int a = 5; int c = ~a; // 结果为11111111111111111111111111111010(在32位系统下,以补码形式表示 -6)
左移(<<)
功能:将操作数的所有位向左移动指定的位数,右边空出的位用0填充。
int a = 5; int c = a << 2; // 5的二进制0101左移2位变为10100,即20
右移(>>)
功能:将操作数的所有位向右移动指定的位数。对于无符号数,左边空出的位用0填充;对于有符号数,如果是算术右移(大多数系统默认),左边空出的位用符号位填充。
int a = 5; int c = a >> 1; // 0101右移1位变为0010,即2
2. 位运算的应用场景
优化算术运算
例如,判断一个数是否为偶数可以使用if (num & 1 == 0),比if (num % 2 == 0)效率更高,因为位运算通常比除法运算快。
操作标志位
在很多情况下,需要用一个整数的不同位来表示不同的状态(标志位)。例如,在一个权限系统中,可以用一个32位整数的不同位来表示用户的不同权限(读、写、执行等)。
数据加密和压缩
某些简单的加密算法和数据压缩算法会用到位运算。例如,异或加密,对数据的每个字节与一个密钥字节进行异或操作来加密数据。
二、bitset
1. 基本概念
bitset是C++标准库中的一个模板类,用于方便地处理固定大小的位序列。它提供了一种比直接使用位运算更方便的方式来操作位。
定义在<bitset>头文件中。
2. 构造与初始化
默认构造
可以创建一个指定大小的bitset,所有位初始化为0。例如:
std::bitset<8> bs1; // 创建一个8位的bitset,初始值为00000000
从无符号整数初始化
可以从一个无符号整数构造bitset,整数的二进制表示被用来初始化bitset。例如:
unsigned int num = 10; // 二进制为1010 std::bitset<8> bs2(num); // 创建一个8位的bitset,值为00001010
从字符串初始化
可以从一个表示二进制数的字符串(只能包含'0'和'1')来初始化bitset。例如:
std::string str = "1010"; std::bitset<4> bs3(str); // 创建一个4位的bitset,值为1010
3. 操作方法
设置和清除位
可以使用set方法设置指定的位为1,reset方法清除指定的位为0。例如:
std::bitset<4> bs; bs.set(1); // 将第2位设置为1,此时bs为0010 bs.reset(1); // 将第2位清除为0,此时bs为0000
测试位的值
使用test方法来测试指定位是否为1。例如:
std::bitset<4> bs(10); // 二进制为1010 if (bs.test(1)) { std::cout << "The second bit is 1" << std::endl; }
位计数
使用count方法来计算bitset中1的个数。例如:
std::bitset<8> bs(25); // 二进制为00011001 std::cout << "The number of 1s in the bitset is: " << bs.count() << std::endl;
转换操作
可以将bitset转换为无符号整数或字符串。例如:
std::bitset<4> bs(10); unsigned int num = static_cast<unsigned int>(bs.to_ulong()); std::string str = bs.to_string();
三、位运算的妙用
1、整数运算优化
(1). 快速乘除2的幂次方
普通乘法:如果要计算int num = 5 * 8;(这里8是2的3次方)。
位运算优化:可以写成int num = 5 << 3;。左移操作直接将数字的二进制表示向左移动相应的位数,相当于乘以2的幂次方,速度更快。
普通除法:计算int result = 24 / 8;。
位运算优化:可以写成int result = 24 >> 3;。右移操作将数字的二进制表示向右移动相应的位数,相当于除以2的幂次方。
(2). 计算余数(对2的幂次方)
例如,计算一个整数num除以8(2的3次方)的余数。
普通计算:int remainder = num % 8;
位运算:int remainder = num & 7;。因为8 - 1 = 7,其二进制为0111,与操作后得到的就是num的低3位,也就是除以8的余数。
2、标志位操作
(1). 定义多个标志位
假设要定义一个表示文件权限的变量,有读(READ)、写(WRITE)和执行(EXECUTE)三种权限。
可以用一个int类型变量,其中每一位表示一种权限。
定义如下:
const int READ = 1 << 0; // 二进制为0001
const int WRITE = 1 << 1; // 二进制为0010
const int EXECUTE = 1 << 2; // 二进制为0100
然后可以用按位或操作来设置权限,例如创建一个具有读和写权限的文件权限变量:
int filePermissions = READ | WRITE;
(2). 检查和修改标志位
检查标志位
假设已经有一个表示文件权限的变量filePermissions,要检查是否有读权限。
可以使用按位与操作:if (filePermissions & READ) { /* 有读权限 */ }
修改标志位
要给已经存在的filePermissions变量添加执行权限。
可以使用按位或操作:filePermissions = filePermissions | EXECUTE;
要移除写权限,可以使用按位与和按位取反操作:filePermissions = filePermissions & (~WRITE);
3、数据转换与提取
RGB颜色值操作:在处理图像颜色时,RGB颜色通常用一个32位整数表示(例如在ARGB格式中,A表示透明度,R表示红色,G表示绿色,B表示蓝色,每个通道占8位)。
(1). 提取通道值
要提取红色通道的值,可以这样做:int color = 0xFF0000FF; // 假设这是一个ARGB颜色值
int red = (color >> 16) & 0xFF;。先将颜色值右移16位,使红色通道的值移到最低8位,然后与0xFF进行与操作,得到红色通道的值。
(2). 修改通道值
要将红色通道的值设置为0,可以这样做:color = color & 0x00FFFFFF;。通过与一个除了红色通道为0,其他通道都为1的掩码进行与操作,就可以将红色通道的值设置为0。
4、简单加密解密
(1) 异或加密
假设要加密一个字符数组char data[] = "Hello";,使用一个简单的密钥char key = 'A';。
可以对每个字符与密钥进行异或操作:
for (int i = 0; i < strlen(data); i++) { data[i] = data[i] ^ key; }
(2) 异或解密
再次使用相同的密钥进行异或操作就可以解密:
for (int i = 0; i < strlen(data); i++) { data[i] = data[i] ^ key; }。因为(a ^ b) ^ b=a。