c++中如何使用bitset处理位运算_c++ bitset常用操作汇总【详解】

std::bitset模板参数是编译期常量位数,非字节数;初始化用整数时低位对齐、高位截断,字符串构造需全为'0''1';访问需用test/set/reset做边界检查,[]不检查越界。

bitset 初始化时要注意模板参数是位数,不是字节数或变量名

很多人误以为 std::bitset 表示“存 8 个字节”,其实它表示**固定长度为 8 位的位容器**,底层通常用 1 个 unsigned long 或更小类型实现(编译器优化)。初始化时传入的整数会被按二进制低位对齐填充,高位补零。

常见错误:用十进制大数初始化导致意外截断

std::bitset<4> b1(15);   // 正确:15 -> 1111,刚好填满 4 位
std::bitset<4> b2(16);   // 截断!16 的二进制是 10000 → 取低 4 位 = 0000
std::bitset<4> b3("1101"); // 正确:字符串构造,支持 0/1 字符
std::bitset<4> b4;         // 默认全 0
  • 字符串构造必须全由 '0''1' 组成,否则抛 std::invalid_argument
  • 不能用 std::bitset b = 0b1010; 这种方式直接赋值字面量(C++20 前不支持),得写成 std::bitset(0b1010)
  • 模板参数 N 必须是编译期常量,不能是变量

用 test()、set()、reset() 操作单一位,别直接用下标赋值

bitset 支持 [] 访问(返回 reference),但**不检查越界**——越界访问是未定义行为(UB),调试时可能看似正常,发布版崩溃。

安全做法始终用 test(pos) 查询、set(pos) 置 1、reset(pos) 置 0,它们会做范围检查(越界抛 std::out_of_ra

nge)。

std::bitset<8> b("10100000");
b.set(0);    // 第 0 位(最右)变 1 → 10100001
b.reset(7);  // 第 7 位(最左)变 0 → 00100001
if (b.test(3)) { /* 检查第 3 位是否为 1 */ }
  • set() 不带参数时置全部位为 1;reset() 不带参数时清零;flip() 全翻转
  • pos 是从右往左数的索引(0-based),即 b[0] 对应最低有效位(LSB)
  • 如果确定不会越界且追求极致性能(如内循环),可用 [],但务必确保 pos

位运算符重载天然支持,但注意优先级和临时对象生命周期

std::bitset 重载了 &|^~>>,用法接近内置整数,但有关键差异:

std::bitset<8> a("10101010"), b("11001100");
auto c = a & b;     // OK:结果是 bitset<8>,值为 "10001000"
auto d = ~a;        // OK:翻转所有位
auto e = a << 2;    // OK:左移 2 位,高位丢弃,低位补 0 → "10101000"
  • 移位操作不循环,超出边界的位直接丢失(不像 rotate
  • 复合赋值如 a &= b 是原地修改,比 a = a & b 更高效(避免拷贝)
  • 注意运算符优先级:a & b == c 等价于 a & (b == c),不是 (a & b) == c —— 务必加括号
  • 不要返回局部 bitset 的引用(比如函数返回 bitset&),它会绑定到已销毁对象

转字符串或整数时,size 超过目标类型宽度会出错

to_ulong()to_ullong() 要求位数 ≤ 目标类型的位宽(如 unsigned long 通常是 32 或 64 位),否则抛 std::overflow_error。而 to_string() 总是安全的,但返回的是高位在前的字符串(符合人类阅读习惯)。

std::bitset<64> big("1111111100000000111111110000000011111111000000001111111100000000");
// auto x = big.to_ulong(); // 在 32 位系统上大概率 overflow
auto y = big.to_ullong(); // 安全(假设 unsigned long long ≥ 64 位)
auto s = big.to_string(); // 返回长度为 64 的 string,s[0] 是最高位
  • to_string() 结果不含前导空格或 0b 前缀,就是纯 '0'/'1' 序列
  • 若需转为十六进制字符串,得自己分组处理(bitset 不提供直接支持)
  • 频繁转换建议缓存结果,因为 to_string() 每次都分配新字符串

实际项目中,bitset 最容易被低估的是它的栈上固定大小特性——没有堆分配、无异常构造(除字符串构造)、零成本抽象。但一旦 size 需要运行时决定,就得切到 vector 或手动管理 uint32_t*,这时候边界检查和内存对齐就变成显式责任了。