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。

C++编程语言基础

C++运算符:位运算 与 bitset类库