C++ でビットマスクを使用する

胡金庫 2023年10月12日
  1. C++ でビットマスクを定義し、日付オブジェクトを実装するには struct を使用する
  2. structunion を用いて C++ でビットマスクを定義し、日付オブジェクトを実装する
  3. C++ でビットマスクを定義するには std::bitset を用いる
C++ でビットマスクを使用する

この記事では、C++ でビットマスクを使用する方法の複数の方法を示します。

C++ でビットマスクを定義し、日付オブジェクトを実装するには struct を使用する

struct は C++ でよく使われるデータ構造体です。しかも、C 言語とは対照的に class とはわずかに異なるだけです。今回は、複数のデータメンバを持つ struct を定義するだけでよいが、変数名と整数を指定した後に : という新しい記法があります。例のコードに示すように、uint32_t 型のデータメンバを持つ struct を宣言し、右の数字の和が 32 であることを示しています。この構成は、struct が単一の uint32_t 型データのサイズをメモリ上で占有し、ユーザがそのビットフィールドの範囲(23:5:4)に個別にアクセスできることを意味します。

この方法は、似たような structs の中に押し込められてしまうかもしれないデータ構造のメモリ使用量を節約するために主に利用されています。また、これは 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

structunion を用いて C++ でビットマスクを定義し、日付オブジェクトを実装する

前述の方法は、ビットマスクを実装するための十分で正しい方法です。しかし、この方法には 1つの欠点があります。それは、メンバへのアクセスや値の代入には組み込み型の操作よりも比較的時間がかかるということです。この問題は、struct と単一の組み込み型変数からなる union 型オブジェクトを実装することで解決することができます。なお、struct のレイアウトは先ほどのメソッドで構築したものと同じです。この場合の違いは、変数 uint32_t ydmstruct と同じサイズを占めることです。

これは、データメンバの初期化や値の割り当てをより速く行うためのアイデアです。つまり、struct に格納されているときのビットのレイアウトと正確に一致するビット演算で整数を構築し、それを単一の uint32_t メンバに代入します。これにより、3つのメンバそれぞれに同じ演算を繰り返すよりも、1 回でデータにアクセスする時間を節約することができます。

ビット単位の演算は比較的簡単です。すなわち、struct の最初のメンバの値から始め、次のメンバの結果を前のメンバが占有するビット数だけ左にシフトしたものを 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

C++ でビットマスクを定義するには std::bitset を用いる

std::bitset はバイナリマスクデータを格納するクラスを含む標準ライブラリ機能です。bitset には複数の便利な操作関数が組み込まれており、宣言は非常に簡単です。bitset はバイナリの string 値や複数の数値値で初期化することができます。ビット単位の演算は組み込みのメソッドであり、オーバーロードされた従来の演算子で呼び出すことができます。なお、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
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook