一、bitset概述
- 标准库定义了bitset类,使得位运算使用更为容易,并且能够处理超过最长整型类型大小的位集合
- bitset定义在头文件bitset中
二、定义和初始化bitset
- 上图给出了bitset的构造方法
- 低位与高位:
- 二进制集合的起始位置(索引为0)称为低位,往后的称为高位
- 注意,bitset的索引0从最右侧开始
- bitset是一个类模板,类似于array类,具有固定的大小,且大小必须是一个常量表达式。当我们定义一个bitset时,需要声明它包含多少个二进制位:
std::bitset<32> bitvec(1U); //U占32位。因此bitvec为32位;低位为1,其他位均为0
用unsigned值初始化bitset
- 当我们使用一个整型值来初始化bitset时,此值会被转换为unsigned long long类型并被当作位模式来处理
- 规则如下:
- 如果bitset的大小大于一个unsigned long long中的二进制位数,则剩余的高位被置位0
- 如果bitset的大小小于一个unsigned long long中的二进制位数,则只使用给定值中的地位,超出bitset大小的高位被丢弃
- 例如:
//0xbeef为:1011 1110 1110 1111
//bitvec1比初始值小。高位被丢弃
std::bitset<13> bitvec1(0xbeef); //二进制位序列为1 1110 1110 1111
//bitvec1比初始值大。高位被置位0
std::bitset<20> bitvec2(0xbeef); //进制位序列为0000 1011 1110 1110 1111
//在64位机器上,long long 0ULL是64位的,因此~0ULL是64个1
std::bitset<128> bitvec3(~0ULL); //0~63位为1,63~127位为0
std::cout << "bitvec1: " << bitvec1 << std::endl;
std::cout << "bitvec2: " << bitvec2 << std::endl;
std::cout << "bitvec3: " << bitvec3 << std::endl;
从一个string初始化bitset
- 我们可以从一个string或一个字符数组指针来初始化bitset
- 注意(重点):当使用字符串表示数时,字符串中下标最小的字符对应高位,下标最大的字符对应低位
- 例如:
std::bitset<32> bitvec4("1100"); //1100
std::cout << "bitvec4 : " << bitvec4 << std::endl;
- 我们也可以使用string的子串来初始化bitset。例如:
std::string str("1111111000000011001101");
std::bitset<32> bitvec5(str, 5, 4); //1100
std::bitset<32> bitvec6(str, str.size() - 4); //1101
std::cout << "bitvec5 : " << bitvec5 << std::endl;
std::cout << "bitvec6 : " << bitvec6 << std::endl;
三、bitset的操作
- 操作大概分为两类:
- 一类返回bitset的信息。例如count、size、all、any、none等
- 另一类用来设置bitset。例如set、reset、flip。这些函数都是重载的
- 例如:
std::bitset<32> bitVec(1U); //32位,低位为1,剩余位为0
bool is_set = bitVec.any(); //true,因为有1被置位
bool is_not_set = bitVec.none(); //false,因为有1被置位
bool all_set = bitVec.all(); //false,因为有1被置位
size_t onBits = bitVec.count(); //返回1
size_t sz = bitVec.size(); //返回31
bitVec.flip(); //反转bitvec中的所有位
bitVec.reset(); //将所有位复位
bitVec.set(); //将所有位复位
- 成员flip、set、reset和test允许我们读写指定位置的位,例如:
std::bitset<32> bitVec(1U); //32位,低位为1,剩余位为0
int i = 5;
bitVec.flip(0); //翻转第1位
bitVec.set(bitVec.size() - 1); //置位第1位
bitVec.set(0, 0); //复位第1位
bitVec.reset(i); //复位第i位
bitVec.test(0); //返回false,因为第1位是复位的
- 下标运算符对const属性进行了重载:
- const版本的下标运算符将所在位的值返回给bool,如果该位为1返回true,该位为0返回falase
- 非const版本返回bitset定义的一个特殊类型,允许我们操纵指定位的值
std::bitset<32> bitVec(1U); //32位,低位为1,剩余位为0
bitVec[0] = 0; //将第1位复位
bitVec[31] = bitVec[0]; //将最后一位设置为与第1位一样
bitVec[0].flip(); //翻转第1位
~bitVec[0]; //等价操作,也是翻转第1位
bool b = bitVec[0]; //将bitVec[0]的值转换为bool类型
提取bitset的值(to_ulong、to_ullong)
- to_ulong和to_ullong操作都返回一个值,保存了与bitset对象相同的位模式
- 只有当bitset的大小<=对应的大小(to_ulong为unsigned long,to_ullong为unsigned long long)时,才能使用这两个操作
- 例如:
//在64位机器上,long long 0ULL是64位的,因此~0ULL是64个1
std::bitset<128> bitvec3(~0ULL); //0~63位为1,63~127位为0
unsigned long ulong = bitvec3.to_ulong();
std::cout << "ulong = " << ulong << std::endl;
bitset的IO运算符
- 输入运算符(>>):
- 从一个输入流读取字符,保存到一个临时的string对象中
- 直到读取的字符数达到bitset对应的大小时,或是遇到不是1或0的字符时,或是遇到文件尾或输入错误时,读取过程才停止
- 读取过程结束之后,立即使用临时string对象来初始化bitset
- 如果读取的字符数小于bitset的大小,与往常一样,高位被置位0
- 输出运算符(<<):
- 例如:
std::bitset<16> bits;
std::cin >> bits;
std::cout << "bits: " << bits << std::endl;
演示案例
- 现在我们有一个程序:假设班级中有30个学生,老师每周都会对学生进行一次检测,检测的结果分为通过和不通过两种。我们使用一个unsigned long来表示,在任何机器上unsigned long都至少为32位,因此可以使用每一位来代替一个学生
- 我们下面先使用最普通的位运算来完成这个功能
bool status;
unsigned long quizA = 0; //此值被当作位集合使用
quizA |= 1UL << 27; //指出第27个学生通过了检测
status = quizA&(1UL << 27); //检查第27个学生示范通过了检测
quizA &= ~(1UL << 27); //第27个学生为通过检测
bool status;
std::bitset<30> quizB; //每个学生分配一位,所有位初始化为0
quizB.set(27); ///指出第27个学生通过了检测
status = quizB[27]; //检查第27个学生示范通过了检测
quizB.reset(04); //第27个学生为通过检测