文章

数据类型 Bitset与位运算

数据类型 Bitset与位运算

[TOC]

C++位运算

按位运算是C++中最基本的位操作,主要用于直接操作数据的二进制表示。C++提供了几种按位运算符:

  • 按位与 &:对两个数的每一对应位进行与运算,只有两个对应位都为1时结果才为1。
  • 按位或 |:对两个数的每一对应位进行或运算,只要其中一个位为1,结果就为1。
  • 按位异或 ^:对两个数的每一对应位进行异或运算,当两个对应位不同时,结果为1。
  • 按位取反 ~:对操作数的每一位进行取反操作,0变为1,1变为0。
  • 左移 <<:将操作数的所有位左移指定的位数,右边用0填充。
  • 右移 >>:将操作数的所有位右移指定的位数,左边用符号位填充(算术右移)或0填充(逻辑右移)。

示例代码如下:1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int main() {
    unsigned int a = 5;  // 00000101
    unsigned int b = 9;  // 00001001

    std::cout << "a & b: " << (a & b) << std::endl; // 1
    std::cout << "a | b: " << (a | b) << std::endl; // 13
    std::cout << "a ^ b: " << (a ^ b) << std::endl; // 12
    std::cout << "~a: " << (~a) << std::endl;       // 4294967290
    std::cout << "a << 1: " << (a << 1) << std::endl; // 10
    std::cout << "b >> 1: " << (b >> 1) << std::endl; // 4

    return 0;
}

在某些情况下,你可能需要访问或修改某个特定位。例如,你可能想设置一个标志位或检查一个寄存器的状态。具体方法见文章1

当你需要同时处理多个位时,位掩码(bitmask)是一个非常有用的工具。通过位掩码,你可以有效地设置、清除或切换多个位。具体方法同样见文章1

bitset

1. bitset简介

C++的 bitset 若要使用的话需要 #include<bitset>,它是一种类似数组的结构,它的每一个元素只能是0或1,每个元素仅用1bit空间,相当于一个char元素所占空间的八分之一。

bitset中的每个元素都能单独被访问,例如对于一个叫做foo的bitset,表达式foo[3]访问了它的第4个元素,就像数组一样。

bitset有一个特性:整数类型和布尔数组都能转化成bitset。

bitset的大小在编译时就需要确定。如果你想要不确定长度的bitset,请使用vector。

2. bitset构造函数

用字符串构造时,字符串只能包含 ‘0’ 或 ‘1’ ,否则会抛出异常。

构造时,需在<>中表明bitset 的大小(即size)。

bitset的大小在编译时就需要确定。

1
2
3
4
5
#include<bitset>
std::bitset<4> foo; //创建一个4位的位集,每一位默认为0,当整数的大小小于位数时,高位填充为0
std::bitset<4> foo(5);  //用整数初始化  5二进制位:101  foo值:0101 当整数的大小超过位数时,从整数二进制的低位开始赋值,高位被舍弃
std::bitset<4> foo(19);  //用整数初始化,19二进制位:10011     foo值:0011
std::bitset<4> foo(std::string("0101")); //字符串初始化,字符串中必须只能含有‘0’/‘1’

在进行有参构造时,若参数的二进制表示比bitset的size小,则在前面用0补充(如上面的栗子);

若比bitsize大,参数为整数时取后面部分,参数为字符串时取前面部分(如下面栗子):

1
2
3
4
5
6
7
8
bitset<2> bitset1(12);  //12的二进制为1100(长度为4),但bitset1的size=2,只取后面部分,即00
string s = "100101";  
bitset<4> bitset2(s);  //s的size=6,而bitset的size=4,只取前面部分,即1001
char s2[] = "11101";
bitset<4> bitset3(s2);  //与bitset2同理,只取前面部分,即1110
cout << bitset1 << endl;  //00
cout << bitset2 << endl;  //1001
cout << bitset3 << endl;  //1110

3. bitset操作函数

3.1 常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 位运算都可以用: 与、或、非、异或,左移,右移
foo&foo2
foo|foo2
~foo
foo^foo2
foo<<=2
foo>>=2
foo.size()      // 返回大小(位数)
foo.count()     // 返回1的个数
foo.any()       // 返回是否有1
foo.none()      // 返回是否没有1
foo.set()       // 全都变成1
foo.set(p)      // 将第p + 1位变成1
foo.set(p, x)   // 将第p + 1位变成x
foo.reset()     // 全都变成0
foo.reset(p)    // 将第p + 1位变成0
foo.flip()      // 全都取反
foo.flip(p)     // 将第p + 1位取反
foo.to_ulong()  // 返回它转换为unsigned long的结果,如果超出范围则报错
foo.to_ullong() // 返回它转换为unsigned long long的结果,如果超出范围则报错
foo.to_string() // 返回它转换为string的结果

3.2 操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));

cout << (foo^=bar) << endl;       // 1010 (foo对bar按位异或后赋值给foo)
cout << (foo&=bar) << endl;       // 0010 (按位与后赋值给foo)
cout << (foo|=bar) << endl;       // 0011 (按位或后赋值给foo)

cout << (foo<<=2) << endl;        // 1100 (左移2位,低位补0,有自身赋值)
cout << (foo>>=1) << endl;        // 0110 (右移1位,高位补0,有自身赋值)

cout << (~bar) << endl;           // 1100 (按位取反)
cout << (bar<<1) << endl;         // 0110 (左移,不赋值)
cout << (bar>>1) << endl;         // 0001 (右移,不赋值)

cout << (foo==bar) << endl;       // false (0110==0011为false)
cout << (foo!=bar) << endl;       // true  (0110!=0011为true)

cout << (foo&bar) << endl;        // 0010 (按位与,不赋值)
cout << (foo|bar) << endl;        // 0111 (按位或,不赋值)
cout << (foo^bar) << endl;        // 0101 (按位异或,不赋值)

3.3 函数

1
2
3
4
5
6
7
8
9
10
11
12
bitset<8> foo ("10011011");
std::bitset<8> bits("1001"); // 初始化位集00001001

cout << foo.count() << endl;  //5  (count函数用来求bitset中1的位数,foo中共有5个1
cout << foo.size() << endl;   //8  (size函数用来求bitset的大小,一共有8位

cout << foo.test(0) << endl;  //true  (test函数用来查下标处的元素是0还是1,并返回false或true,此处foo[0]为1,返回true
cout << foo.test(2) << endl;  //false  (同理,foo[2]为0,返回false

cout << foo.any() << endl;  //true  (any函数检查bitset中是否有1
cout << foo.none() << endl;  //false  (none函数检查bitset中是否没有1
cout << foo.all() << endl;  //false  (all函数检查bitset中是全部为1

可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,当然通过这种方式对某一位元素赋值也是可以的:

1
2
3
4
5
bitset<4> foo ("1011");

cout << foo[0] << endl;  //1
cout << foo[1] << endl;  //1
cout << foo[2] << endl;  //0

补充说明一下:test函数会对下标越界作出检查,而通过 [ ] 访问元素却不会经过下标检查,所以,在两种方式通用的情况下,选择test函数更安全一些。

3.4 类型转换函数

1
2
3
4
5
6
7
8
9
bitset<8> foo ("10011011");

string s = foo.to_string();              //将bitset转换成string类型
unsigned long a = foo.to_ulong();        //将bitset转换成unsigned long类型
unsigned long long b = foo.to_ullong();  //将bitset转换成unsigned long long类型

cout << s << endl;  //10011011
cout << a << endl;  //155
cout << b << endl;  //155

参考文章23

本文由作者按照 CC BY 4.0 进行授权