在 C++ 中使用位掩码

Jinku Hu 2023年1月30日 2021年1月4日
  1. 使用 struct 在 C++ 中定义位掩码并实现日期对象
  2. 使用结构体和 union 在 C++ 中定义位掩码和实现 Date 对象
  3. 使用 std::bitset 在 C++ 中定义比特掩码
在 C++ 中使用位掩码

本文将演示如何在 C++ 中使用位掩码的多种方法。

使用 struct 在 C++ 中定义位掩码并实现日期对象

struct 是 C++ 中流行的一种数据结构。而且,相对于 C 语言中的 class,它只是与 class 略有不同。在这种情况下,我们只需要定义有多个数据成员的 struct,但在每个变量名和指定的整数后面都使用了一个新的符号:。如示例代码所示,我们声明了一个带有 uint32_t 类型数据成员的 struct,右边的数字之和是 32。这种结构意味着 struct 在内存中占据了单个 uint32_t 数据类型的大小,用户可以分别访问它的位域范围(23:5:4)。

这种方法主要是为了节省可能被挤进类似结构体的数据结构的内存利用率。这也是 C++ 编程语言中访问比一个字节更小的数据的常用方法之一。请注意,第一个成员 year 只能容纳 223-1 以内的整数值,其他成员也是如此。

#include <iostream>

using std::cout;
using std::endl;

struct {
    uint32_t year:23;
    uint32_t day:5;
    uint32_t month:4;
} typedef Bitfield;

int main() {
    Bitfield date = {2020, 13, 12};

    cout << "sizeof Bitfield: " << sizeof(date) << " bytes\n";
    cout << "date: " << date.day << "/"
            << date.month << "/" << date.year << endl;

    return EXIT_SUCCESS;
}

输出:

sizeof Bitfield: 4 bytes
date: 13/12/2020

使用结构体和 union 在 C++ 中定义位掩码和实现 Date 对象

前面的方法是实现位掩码的一个充分和正确的方法。不过,它还是有一个缺点-访问和给成员赋值比对内置类型的操作相对要花费更多的时间。这个问题可以通过实现一个由 struct 和一个内置类型变量组成的 union 类型对象来解决。需要注意的是,struct 的布局和我们在前面的方法中构造的是一样的。在这种情况下,不同的是变量 uint32_t ydmstruct 占据相同的大小。

这个想法是为了更快地初始化/分配数据成员的值。也就是说,我们用位运算构造一个整数,当它们存储在结构体中时,完全匹配位的布局,然后将其分配给一个 uint32_t 成员。这样可以节省一次访问数据的时间,而不是对三个成员分别重复同样的操作。

比特运算比较简单,即我们从结构体中第一个成员的值开始,用下一个成员的结果按前一个成员所占的位数向左移位进行 OR 运算,接下来的运算也是如此。

#include <iostream>

using std::cout;
using std::endl;

union BitfieldFast{
    struct {
        uint32_t year:23;
        uint32_t day:5;
        uint32_t month:4;
    };
    uint32_t ydm;
};

int main() {
    BitfieldFast date_f{};
    date_f.ydm = 2020 | (13 << 23) | (12 << 28);

    cout << "sizeof BitfieldFast: " << sizeof(date_f) << " bytes\n";
    cout << "date: " << date_f.day << "/"
            << date_f.month << "/" << date_f.year << endl;

    return EXIT_SUCCESS;
}
sizeof BitfieldFast: 4 bytes
date: 13/12/2020

使用 std::bitset 在 C++ 中定义比特掩码

std::bitset 是一个标准的库功能,它包含了存储二进制掩码数据的类。bitset 内置了多个有用的操作函数,声明起来相当轻松。它可以用一个二进制字符串值或多个数字值进行初始化。位元操作是内置方法,可以用传统的操作符重载调用。注意,reset 方法会永久修改它所调用的 bitset

#include <iostream>
#include <bitset>

using std::cout;
using std::endl;

int main() {
    std::bitset<16> bs1("1100100011110010");
    std::bitset<16> bs2(0xfff0);
    cout << "bs1        : " << bs1 << endl;
    cout << "bs2        : " << bs2 << endl;
    cout << "NOT bs1    : " << ~bs1 << endl;
    cout << "bs1 AND bs2: " << (bs1 & bs2) << endl;
    cout << "bs1 reset  : " << bs1.reset() << endl;

    return EXIT_SUCCESS;
}

输出:

bs1        : 1100100011110010
bs2        : 1111111111110000
NOT bs1    : 0011011100001101
bs1 AND bs2: 1100100011110000
bs1 reset  : 0000000000000000
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn