bitset类型
我们介绍了将整型运算对象当作二进制位集合处理的一些内置运算符。
- 标准库还定义了bitset类,使得位运算的使用更为容易,并且能够处理超过最长整型类型大小的位集合。
- bitset类定义在头文件bitset中。
定义和初始化bitset
bitset类是一个类模板,它类似array类,具有固定的大小。当我们定义一个bitset时,需要声明它包含多少个二进制位:
bitset<32>bitvec(10);// 32位;低位为 1,其他位为 0
大小必须是一个常量表达式。
这条语句定义bitvec为一个包含32位的bitset。就像 vector 包含未命名的元素一样,bitset中的二进制位也是未命名的,我们通过位置来访问它们。
二进制位的位置是从0开始编号的。
因此,bitvec包含编号从0到31的32个二进制位。编号从0开始的二进制位被称为低位,编号到31结束的二进制位被称为高位。
bitset<n>b; | b有n位;每一位均为0。此构造函数是一个constexpr |
bitset<n> b(u); | b是unsigned lonq long值u的低n位的拷贝。如果n大于 unsigned long lonq的大小,则b中超出unsigned long long 的高位被置为 0。此构造函数是一个 constexpr |
bitset<n> b(s, pos,zero,one); | b是string s从位置pos开始m个字符的拷贝。 s只能包含zero和one; 字符如果s包含任何其他字符,构造函数会抛出invalid_argument 异常。 字符在b中分别保存为 zero和one。 pos 默认为 0,m 默认为 string::npos, zero 默认为'0',one默认为'1’ |
bitset<n> b(cP, pos,m, zero, one); | 与上一个构造函数相同,但从cp指向的字符数组中拷贝字符。
|
接受一个string或一个字符指针的构造函数是explicit的。在新标准中增加了为0和1指定其他字符的功能。
用 unsigned 值初始化 bitset
当我们使用一个整型值来初始化bitset时,此值将被转换为 unsigned long long类型并被当作位模式来处理。
bitset中的二进制位将是此模式的一个副本。
- 如果bitset的大小大于一个unsigned long long中的二进制位数,则剩余的高位被置为0。
- 如果bitset的大小小于一个unsigned long long中的二进制位数,则只使用给定值中的低位,超出bitset大小的高位被丢弃:
#include<iostream>
#include<bitset>
using namespace std;
int main()
{
// bitvec1比初始值小;初始值中的高位被丢弃
bitset<13> bitvec1(0xbeef);// 二进制位序列为 1111011101111
cout <<"bitvec1:"<< bitvec1 << endl;
//bitvec2比初始值大;它的高位被置为0
bitset<20> bitvec2(0xbeef);//二进制位序列为00001011111011101111
cout << "bitvec2:" << bitvec2 << endl;
// 在64位机器中,long long OULL是64个0比特,因此~0ULL是64个1
bitset<128> bitvec3(~0ULL);//0~63位为1;63~127位为0
cout << "bitvec3:" << bitvec3 << endl;
}
从一个string 初始化bitset
我们可以从一个string或一个字符数组指针来初始化bitset。
两种情况下,字符都直接表示位模式。
与往常一样,当我们使用字符串表示数时,字符串中下标最小的字符对应高位,反之亦然:
bitset<32> bitvec4("1100”);// 2、3两位为1,剩余两位为0
如果string包含的字符数比bitset少,则bitset的高位被置为0。
string的下标编号习惯与bitset恰好相反:string中下标最大的字符(最右字符)用来初始化bitset中的低位(下标为0的二进制位)。当你用一个string初始化一个bitset时,要记住这个差别。
我们不必使用整个string来作为bitset的初始值,可以只用一个子串作为初始值:
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4);//从str[5]开始的四个二进制位,1100
cout << bitvec5 << endl;
bitset<32> bitvec6(str, str.size() - 4); //使用最后四个字符
cout << bitvec6 << endl;
- 此处,bitvec5用str中从str[5]开始的长度为4的子串进行初始化。与往常一样,子串的最右字符表示最低位。因此,bitvec5中第3位到第0位被设置为1100,剩余位被设置为0。
- 传递给bitvec6的初始值是一个string和一个开始位置,因此bitvec6用str中倒数第四个字符开始的子串进行初始化。bitvec6中剩余二进制位被初始化为0。
下图说明了这两个初始化过程
bitset 操作
bitset类定义了多种检测或设置一个或多个二进制位的方法。
bitset类还支持我们介绍过的位运算符,这些运算符用于bitset对象的含义与内置运算符用于unsigned运算对象相同。
b.any() | b 中是否存在置位的二进制位 |
b.all() | b 中所有位都置位了吗 |
b.none() | b 中不存在置位的二进制位吗 |
b.count() | b中置位的位数 |
b.size() | 一个constexpr函数,返回b中的位数 |
b.test(pos) | 若pos位置的位是置位的,则返回true,否则返回false |
b.set (pos,v) b.set() | 将位置 pos 处的位设置为bool 值v,v默认为true。 如果未传递实参,则将b中所有位置位 |
b.reset (pos) b.reset() | 将位置 pos处的位复位或将b中所有位复位 |
b.flip(pos) b.flip() | 改变位置 pos 处的位的状态或改变b中每一位的状态 |
b[pos] | 访问b中位置pos处的位;如果b是const的,则当该位置位时b[pos]返回一个boo1值true,否则返回false |
b.to_ulong() b.to_ullong() | 返回一个unsigned long或一个unsigned long long值,其位模式与b相同。 如果b中位模式不能放入指定的结果类型,则抛出一个overflow_error异常 |
b.to_string(zero, one) | 返回一个string,表示b中的位模式。 zero和one的默认值分别为0和1,用来表示b中的0和1 |
os << b | 将b中二进制位打印为字符1或0,打印到流os |
is >> b | 从is读取字符存入b。当下一个字符不是1或0时,或是已经读入b.size()个位时,读取过程停止 |
count、size、all、any 和none 等几个操作都不接受参数,返回整个bitset的状态。
其他操作——set、reset和flip则改变bitset的状态。改变bitset状态的成员函数都是重载的。
对每个函数,不接受参数的版本对整个集合执行给定的操作;接受一个位置参数的版本则对指定位执行操作:
cout << boolalpha;
bitset<32> bitvec(1U); // 32位;低位为1,剩余位为0
bool is_set = bitvec.any(); // true,因为有1位置位
cout << is_set << endl;
bool is_not_set = bitvec.none(); // false,因为有1位置位了
cout << is_not_set << endl;
bool all_set = bitvec.all(); // false,因为只有1位置位
cout << all_set << endl;
size_t onBits = bitvec.count(); // 返回1
cout << onBits << endl;
size_t sz = bitvec.size(); // 返回32
cout << sz << endl;
bitvec.flip(); // 翻转bitvec中的所有位
cout << bitvec << endl;
bitvec.reset();// 将所有位复位
cout << bitvec << endl;
bitvec.set(); // 将所有位置位
cout << bitvec << endl;
cout << noboolalpha;
当bitset对象的一个或多个位置位(即,等于1)时,操作any返回true。
相反,当所有位复位时,none返回true。新标准引入了all操作,当所有位置位时返回true。
操作count和size返回 size_t类型的值,分别表示对象中置位的位数或总位数。
函数size是一个constexpr函数,因此可以用在要求常量表达式的地方。
成员flip、set、 reset及test允许我们读写指定位置的位:
string str("111111000000");
bitset<12> bitvec(str);
cout << bitvec << endl;
bitvec.flip(0); // 翻转第一位
cout << bitvec << endl;
bitvec.set(bitvec.size() - 1);//置位最后一位
cout << bitvec << endl;
bitvec.set(0, 0); //复位第一位
cout << bitvec << endl;
bitvec.reset(1); //复位第1位
cout << bitvec << endl;
bitvec.test(0); //返回false,因为第一位是复位的
cout << bitvec << endl;
下标运算符对const属性进行了重载。
const版本的下标运算符在指定位置位时返回true,否则返回 false。
非 const版本返回bitset定义的一个特殊类型,它允许我们操纵指定位的值:
string str("111111000000");
bitset<12> bitvec(str);
cout << bitvec << endl;
bitvec[0] = 1; // 将第一位复位
cout << bitvec << endl;
bitvec[11] = bitvec[0]; // 将最后一位设置为与第一位一样
cout << bitvec << endl;
bitvec[0].flip(); // 翻转第一位
cout << bitvec << endl;
~bitvec[0]; //等价操作,也是翻转第一位
cout << bitvec << endl;
bool b = bitvec[0]; //将bitvec[0]的值转换为bool类型
cout << bitvec << endl;
提取 bitset的值
to_ulong和to _ullong操作都返回一个值,保存了与bitset对象相同的位模式。
只有当bitset的大小小于等于对应的大小(to_ulong为unsigned long, to_ullong为unsigned long long)时,我们才能使用这两个操作:
string str("111111000000");
bitset<12> bitvec(str);
bool b = bitvec[0]; //将bitvec[0]的值转换为bool类型
cout << bitvec << endl;
如果 bitset 中的值不能放入给定类型中,则这两个操作会抛出一个overflow_error异常。
bitset 的IO运算符
输入运算符从一个输入流读取字符,保存到一个临时的string对象中。
直到读取的字符数达到对应bitset的大小时,或是遇到不是1或0的字符时,或是遇到文件尾或输入错误时,读取过程才停止。
随即用临时string对象来初始化bitset。
如果读取的字符数小于bitset的大小,则与往常一样,高位将被置为0。
输出运算符打印一个bitset对象中的位模式:
bitset<16> bits;
cin >>bits; //从cin读取最多16个0或1
cout << "bits:" << bits << endl;// 打印刚刚读取的内容
bitset与位操作符
std::bitset
类在 C++ 中为处理固定大小的位序列提供了方便。
使用 std::bitset
时,你可以直接对其应用位操作符,如按位与 (&
)、按位或 (|
)、按位异或 (^
)、按位取反 (~
)、左移 (<<
) 和右移 (>>
)。
下面是一些使用 std::bitset
和位操作符的示例:
#include <iostream>
#include <bitset>
int main() {
// 创建两个8位的bitset对象
std::bitset<8> b1(42); // 00101010
std::bitset<8> b2(25); // 00011001
// 使用位操作符
std::bitset<8> b3 = b1 & b2; // 按位与: 00001000
std::bitset<8> b4 = b1 | b2; // 按位或: 00111011
std::bitset<8> b5 = b1 ^ b2; // 按位异或: 00110011
std::bitset<8> b6 = ~b1; // 按位取反: 11010101
std::bitset<8> b7 = b1 << 2; // 左移两位: 10101000
std::bitset<8> b8 = b2 >> 1; // 右移一位: 00001100
// 输出结果
std::cout << "b1: " << b1 << std::endl;
std::cout << "b2: " << b2 << std::endl;
std::cout << "b3 (b1 & b2): " << b3 << std::endl;
std::cout << "b4 (b1 | b2): " << b4 << std::endl;
std::cout << "b5 (b1 ^ b2): " << b5 << std::endl;
std::cout << "b6 (~b1): " << b6 << std::endl;
std::cout << "b7 (b1 << 2): " << b7 << std::endl;
std::cout << "b8 (b2 >> 1): " << b8 << std::endl;
return 0;
}
在这个示例中:
b1
和b2
是两个std::bitset<8>
对象,分别初始化为十进制数 42 和 25 的二进制表示。b3
是b1
和b2
的按位与结果。b4
是b1
和b2
的按位或结果。b5
是b1
和b2
的按位异或结果。b6
是b1
的按位取反结果。b7
是b1
左移两位的结果。b8
是b2
右移一位的结果。
输出将会是这些操作的二进制结果。
注意,当使用位操作符时,结果的位数与操作数的位数相同。如果左移或右移操作导致某些位丢失,那么这些位将被简单地丢弃。同样,如果左移操作导致在右侧产生新的位,那么这些位将被设置为 0。