C++ 位运算 std::bitset类的使用介绍

简介: C++ 位运算 std::bitset类的使用介绍

简介

std::bitset<N> 是 C++ 标准库中的一个模板类,用于处理固定大小的位序列。这个类提供了一些方便的方法来操作位,例如设置、重置、翻转位等。

在这个上下文中,std::bitset<64> 创建了一个可以存储 64 位的位集。构造函数接受一个 unsigned long long 类型的参数,将其作为位集的初始值。这意味着,如果你有一个 uint64_t 类型的值(如声道布局的位掩码),你可以直接将其传递给 std::bitset<64> 的构造函数,它会将这个值的二进制表示形式作为位集的初始状态。

然后,你可以使用 bitsetoperator[] 来访问特定的位。例如,layout_bitset[i] 将返回位集中第 i 位的值。这个操作符返回一个 bitset::reference 类型的值,它可以自动转换为 bool,所以你可以直接在 if 语句中使用它。

总的来说,std::bitset 是一个非常方便的工具,可以让你更容易地处理位级别的操作。

相关接口

std::bitset 是一个非常有用的类,它提供了一系列的函数来操作和查询位集。以下是一些你可能会觉得有用的函数:

  1. bitset::size(): 返回位集中位的数量。
  2. bitset::count(): 返回位集中设置(即值为1)的位的数量。
  3. bitset::set(): 将位集中的所有位都设置为1。
  4. bitset::set(size_t pos, bool val = true): 将位集中指定位置的位设置为给定的值。
  5. bitset::reset(): 将位集中的所有位都重置为0。
  6. bitset::reset(size_t pos): 将位集中指定位置的位重置为0。
  7. bitset::flip(): 翻转位集中的所有位(即将所有的1变为0,将所有的0变为1)。
  8. bitset::flip(size_t pos): 翻转位集中指定位置的位。
  9. bitset::test(size_t pos): 检查位集中指定位置的位是否被设置。如果该位被设置,则返回 true;否则,返回 false
  10. bitset::operator[]: 通过下标访问位集中的位。例如,bitset[3] 会返回位集中第3位的值。
  11. bitset::to_string(): 将位集转换为字符串,其中每个字符代表一个位。
  12. bitset::to_ulong(), bitset::to_ullong(): 将位集转换为 unsigned longunsigned long long 类型的值。如果位集太大,无法转换为这些类型,那么这些函数会抛出 std::overflow_error 异常。

这些函数使得 std::bitset 成为处理位级别数据的强大工具。

std::bitset 的高级用法

std::bitset 是一个非常强大的工具,可以用于处理位级别的操作。除了基本的设置和获取位之外,它还提供了一些高级的功能。以下是一些 std::bitset 的高级用法:

  1. 位操作符std::bitset 支持位操作符 &|^~,以及相应的复合赋值操作符 &=|=^=。这些操作符可以用于执行位级别的 AND、OR、XOR 和 NOT 操作。
std::bitset<4> b1("1100");
std::bitset<4> b2("1010");
std::bitset<4> b3 = b1 & b2; // bitwise AND
std::bitset<4> b4 = b1 | b2; // bitwise OR
std::bitset<4> b5 = b1 ^ b2; // bitwise XOR
std::bitset<4> b6 = ~b1;     // bitwise NOT
  1. 位移操作符std::bitset 支持位移操作符 <<>>,以及相应的复合赋值操作符 <<=>>=。这些操作符可以用于执行位级别的左移和右移操作。
std::bitset<4> b1("1100");
std::bitset<4> b2 = b1 << 1; // left shift
std::bitset<4> b3 = b1 >> 1; // right shift
  1. 比较操作符std::bitset 支持比较操作符 ==!=。这些操作符可以用于比较两个 bitset 是否相等。
std::bitset<4> b1("1100");
std::bitset<4> b2("1010");
bool equal = (b1 == b2); // compare bitsets
  1. 其他有用的成员函数std::bitset 还提供了一些其他有用的成员函数,如 count()(返回设置的位数)、size()(返回位数)、test(size_t pos)(检查指定位置的位是否设置)、any()(检查是否有位设置)、none()(检查是否没有位设置)和 flip()(反转所有位)等。
std::bitset<4> b1("1100");
size_t count = b1.count(); // count set bits
size_t size = b1.size();   // get number of bits
bool bit = b1.test(2);     // test bit at position 2
bool any = b1.any();       // check if any bit is set
bool none = b1.none();     // check if no bit is set
b1.flip();                 // flip all bits

以上就是 std::bitset 的一些高级用法。希望这些信息对你有所帮助!

底层实现

std::bitset 是一个模板类,它的构造函数有多种形式,可以接受不同类型的参数。以下是一些主要的构造函数:

  1. 默认构造函数:构造一个所有位都被设置为零的 bitset
  2. 从无符号长整型构造:初始化 bitset 的前 M 位(最右边,最低有效位)为 val 的对应位值,其中 M 是无符号长整型的位数和 bitset 的位数 N 中的较小者。如果 M 小于 N(bitset 比 32 位(直到 C++11)或 64 位(从 C++11 开始)长,对于典型的无符号长整型实现),剩余的位位置被初始化为零。
  3. 从字符串构造:使用 std::basic_string 中的字符构造 bitset。可以提供可选的起始位置 pos 和长度 n,以及表示设置(一)和未设置(零)位的替代字符。Traits::eq() 用于比较字符值。初始化字符串的有效长度是 min(n, str.size() - pos)。如果 pos > str.size(),此构造函数会抛出 std::out_of_range 异常。如果 str 中检查的任何字符都不是零或一,它会抛出 std::invalid_argument 异常。
  4. 类似于 (3),但使用 CharT* 而不是 std::basic_string。等价于 bitset(n == basic_string<CharT>::npos ? basic_string<CharT>(str) : basic_string<CharT>(str, n), 0, n, zero, one)

std::bitset 的底层实现依赖于编译器和平台,但基本的思想是使用一种紧凑的数据结构(通常是一个或多个无符号整数)来存储位。构造函数和其他成员函数则负责处理位的设置、清除和查询等操作。

以下是一个简单的示例,展示了如何使用不同的 std::bitset 构造函数:

#include <bitset>
#include <string>
#include <iostream>
#include <climits>
int main()
{
    // empty constructor
    std::bitset<8> b1; // [0,0,0,0,0,0,0,0]
    // unsigned long long constructor
    std::bitset<8> b2(42);          // [0,0,1,0,1,0,1,0]
    std::bitset<70> bl(ULLONG_MAX); // [0,0,0,0,0,0,1,1,1,...,1,1,1] in C++11
    std::bitset<8> bs(0xfff0);      // [1,1,1,1,0,0,0,0]
    // string constructor
    std::string bit_string = "110010";
    std::bitset<8> b3(bit_string);       // [0,0,1,1,0,0,1,0]
    std::bitset<8> b4(bit_string, 2);    // [0,0,0,0,0,0,1,0]
    std::bitset<8> b5(bit_string, 2, 3); // [0,0,0,0,0,0,0,1]
    // string constructor using custom zero/one digits
    std::string alpha_bit_string = "aBaaBBaB";
    std::bitset<8> b6(alpha_bit_string, 0, alpha_bit_string.size(),
                      'a', 'B');         // [0,1,0,0,1,1,0,1]
    // char* constructor using custom digits
    std::bitset<8> b7("XXXXYYYY", 8, 'X', 'Y'); // [0,0,0,0,1,1,1,1]
    std::cout <<   "b1: " << b1 << "\nb2: " << b2 << "\nbl: " << bl
              << "\nbs: " << bs << "\nb3: " << b3 << "\nb4: " << b4
              << "\nb5: " << b5 << "\nb6: " << b6 << "\nb7: " << b7 << '\n';
}

这个示例创建了几个 std::bitset 对象,使用了不同的构造函数和参数。然后,它将每个 bitset 对象的内容打印到控制台。

使用场景

std::bitset 是一个非常有用的工具,它可以用于处理和操作位级别的数据。以下是一些 std::bitset 可能的使用场景:

  1. 位标志和开关:如果你有一组可以打开和关闭的标志,你可以使用 std::bitset 来存储它们。例如,你可能有一个配置对象,其中每个配置选项都可以打开或关闭。使用 std::bitset,你可以在一个紧凑的数据结构中存储所有这些选项的状态。
  2. 位掩码:位掩码是一种常见的编程技术,用于设置、清除和切换位。std::bitset 提供了一种方便的方式来处理位掩码。
  3. 二进制表示:如果你需要以二进制形式处理数据,std::bitset 是一个很好的工具。例如,你可能需要读取或写入二进制文件,或者你可能需要以二进制形式显示数据。
  4. 优化存储:如果你有一个大型数据集,其中每个元素只需要几位来存储,那么 std::bitset 可以帮助你节省空间。例如,如果你有一个包含几百万个元素的数组,每个元素都是一个小于 16 的整数,那么你可以使用 std::bitset 来存储这个数组,而不是使用一个整数数组。
  5. 加密和解密:在某些加密和解密算法中,需要对位进行操作。std::bitset 提供了一种方便的方式来处理这些操作。
  6. 网络编程:在网络编程中,数据通常以位的形式发送和接收。std::bitset 可以帮助处理这种位级别的数据。
  7. 硬件接口:在与硬件接口的编程中,通常需要对位进行操作,例如设置硬件寄存器的特定位。std::bitset 提供了一种方便的方式来处理这些操作。

总的来说,任何需要处理位级别数据的场景,std::bitset 都可能是一个有用的工具。

std::bitset 的线程完全性问题

std::bitset 是一个类模板,它提供了一种固定大小的位序列。它不提供任何内置的线程安全保证。这意味着,如果你在多个线程中同时访问或修改同一个 std::bitset 实例,你需要自己负责同步。

例如,如果你有一个 std::bitset,并且你在一个线程中设置位,同时在另一个线程中读取位,那么你可能会遇到数据竞争,这是一种未定义的行为。为了避免这种情况,你需要使用某种同步原语,如 std::mutex,来确保在任何时候只有一个线程可以访问 bitset

以下是一个简单的例子,展示了如何使用 std::mutex 来保护 std::bitset 的访问:

#include <bitset>
#include <thread>
#include <mutex>
std::bitset<8> bits;
std::mutex mtx;
void setBits()
{
    std::lock_guard<std::mutex> lock(mtx);
    bits.set();
}
void printBits()
{
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << bits << std::endl;
}
int main()
{
    std::thread t1(setBits);
    std::thread t2(printBits);
    t1.join();
    t2.join();
    return 0;
}

在这个例子中,setBitsprintBits 函数都使用了 std::lock_guard 来锁定 mtx。这确保了在任何时候只有一个线程可以访问 bits。当 lock_guard 被销毁(即当它离开其作用域)时,它会自动释放锁,这使得锁的管理变得更加容易。

std::bitset 类的优缺点

std::bitset是C++标准库中的一个类,它可以存储和操作固定大小的位序列。

优点:

  1. 高效存储:位集可以有效地存储和处理大量的位,比其他容器类型使用更少的内存空间。
  2. 高效操作:位集提供了多种用于操作位的成员函数,如位反转、位计数等。
  3. 简化代码:使用位集可以简化与位操作相关的代码,提高代码的可读性和可维护性。

缺点:

  1. 固定大小:位集的大小在编译时确定,不能在运行时动态调整。如果需要处理动态大小的位序列,可能需要使用其他数据结构,如std::vector<bool>
  2. 功能限制:位集的功能相比于其他数据结构可能更为有限。例如,它没有提供排序或查找特定元素的直接方法。
  3. 与其他数据类型的互操作性:std::bitset不像标准容器那样可以与其他STL容器或数据类型进行流畅的互操作。
目录
相关文章
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
26 4
|
30天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
30天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
30天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
16 0
|
1月前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3