在 C++ 中使用位掩碼

Jinku Hu 2023年10月12日
  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 <bitset>
#include <iostream>

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
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 創辦人。Jinku 在機器人和汽車行業工作了8多年。他在自動測試、遠端測試及從耐久性測試中創建報告時磨練了自己的程式設計技能。他擁有電氣/ 電子工程背景,但他也擴展了自己的興趣到嵌入式電子、嵌入式程式設計以及前端和後端程式設計。

LinkedIn Facebook