在C++标准库中,bitset是一个固定大小的位序列容器,专门用于处理二进制位的存储和操作,相比手动实现位运算逻辑,它提供了更简洁的接口和更高的可读性,广泛应用于位图、状态标记、权限控制等场景。

bitset的基础定义与初始化
使用bitset需要先引入<bitset>头文件,其模板参数需要指定位的大小,也就是容器能存储的二进制位数量,这个大小在编译期就必须确定,无法动态修改。
常见的初始化方式有以下几种:
#include <bitset>
#include <iostream>
#include <string>
using namespace std;
int main() {
// 初始化为全0,大小为8位
bitset<8> bs1;
// 用整数初始化,十进制10对应二进制00001010
bitset<8> bs2(10);
// 用二进制字符串初始化
bitset<8> bs3("10101010");
// 用十六进制字符串初始化,0x1a对应二进制00011010
bitset<8> bs4("0x1a");
cout << "bs1: " << bs1 << endl;
cout << "bs2: " << bs2 << endl;
cout << "bs3: " << bs3 << endl;
cout << "bs4: " << bs4 << endl;
return 0;
}
输出结果如下:
bs1: 00000000 bs2: 00001010 bs3: 10101010 bs4: 00011010
bitset的常用操作接口
bitset提供了丰富的成员函数,覆盖位访问、位修改、位运算等多种需求,下面介绍最常用的几类操作。
位访问与修改
可以通过下标运算符访问指定位置的位,也可以通过成员函数修改位的状态:
#include <bitset>
#include <iostream>
using namespace std;
int main() {
bitset<8> bs("00000000");
// 访问第3位(从0开始计数)
cout << "第3位初始值: " << bs[3] << endl;
// 设置第3位为1
bs.set(3);
cout << "设置第3位后: " << bs << endl;
// 翻转第3位
bs.flip(3);
cout << "翻转第3位后: " << bs << endl;
// 重置第3位为0
bs.reset(3);
cout << "重置第3位后: " << bs << endl;
// 全部设置为1
bs.set();
cout << "全部置1后: " << bs << endl;
// 全部重置为0
bs.reset();
cout << "全部置0后: " << bs << endl;
return 0;
}
位运算操作
bitset支持按位与、按位或、按位异或、按位取反等常见位运算,操作方式和内置整数类型一致:
#include <bitset>
#include <iostream>
using namespace std;
int main() {
bitset<8> bs1("00001111");
bitset<8> bs2("00110011");
// 按位与
bitset<8> and_res = bs1 & bs2;
cout << "按位与结果: " << and_res << endl;
// 按位或
bitset<8> or_res = bs1 | bs2;
cout << "按位或结果: " << or_res << endl;
// 按位异或
bitset<8> xor_res = bs1 ^ bs2;
cout << "按位异或结果: " << xor_res << endl;
// 按位取反
bitset<8> not_res = ~bs1;
cout << "按位取反结果: " << not_res << endl;
return 0;
}
状态查询接口
bitset还提供了查询整体状态的方法,方便快速判断位集合的情况:
count():返回值为1的位的数量size():返回位的总数量any():判断是否存在至少一个位为1none():判断是否所有位都为0all():判断是否所有位都为1
示例代码如下:
#include <bitset>
#include <iostream>
using namespace std;
int main() {
bitset<8> bs("00010101");
cout << "位总数: " << bs.size() << endl;
cout << "1的数量: " << bs.count() << endl;
cout << "是否存在1: " << bs.any() << endl;
cout << "是否全0: " << bs.none() << endl;
cout << "是否全1: " << bs.all() << endl;
return 0;
}
bitset与手动位运算的对比
在没有bitset之前,开发者通常会用整数配合位运算符手动实现位操作,比如用int类型存储8个状态,每个位代表一个状态:
#include <iostream>
using namespace std;
int main() {
int status = 0;
// 设置第3位为1,1<<3表示第3位为1,其余为0
status |= (1 << 3);
cout << "设置第3位后status: " << status << endl;
// 判断第3位是否为1
if (status & (1 << 3)) {
cout << "第3位为1" << endl;
}
// 重置第3位为0
status &= ~(1 << 3);
cout << "重置第3位后status: " << status << endl;
return 0;
}
对比两种方式,bitset的优势在于:
- 接口更直观,不需要手动计算位移和掩码
- 支持超过内置整数类型的位长度,比如bitset<1000>可以存储1000位
- 提供了
count、any等便捷的状态查询方法,减少重复代码
不过bitset的大小是编译期固定的,如果需要动态大小的位集合,可以考虑使用vector<bool>或者boost::dynamic_bitset。
bitset实际应用场景
bitset最常见的应用是位图场景,比如海量数据的去重、状态标记等。例如需要判断1到1000的数字中哪些出现过,用bitset<1001>就可以直接标记每个数字的状态:
#include <bitset>
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 标记1到1000的数字是否出现
bitset<1001> exist;
vector<int> nums = {12, 45, 78, 12, 90, 45};
for (int num : nums) {
exist.set(num);
}
// 输出出现过的数字
cout << "出现过的数字: ";
for (int i = 1; i <= 1000; i++) {
if (exist.test(i)) {
cout << i << " ";
}
}
cout << endl;
return 0;
}
这种方式相比用数组或者哈希表存储,内存占用会小很多,每个数字只需要1位存储,1000个数字只需要125字节左右的空间。