C++ でビットマスクを使用する
-
C++ でビットマスクを定義し、日付オブジェクトを実装するには
struct
を使用する -
struct
とunion
を用いて C++ でビットマスクを定義し、日付オブジェクトを実装する -
C++ でビットマスクを定義するには
std::bitset
を用いる
この記事では、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
struct
と union
を用いて C++ でビットマスクを定義し、日付オブジェクトを実装する
前述の方法は、ビットマスクを実装するための十分で正しい方法です。しかし、この方法には 1つの欠点があります。それは、メンバへのアクセスや値の代入には組み込み型の操作よりも比較的時間がかかるということです。この問題は、struct
と単一の組み込み型変数からなる union
型オブジェクトを実装することで解決することができます。なお、struct
のレイアウトは先ほどのメソッドで構築したものと同じです。この場合の違いは、変数 uint32_t ydm
が struct
と同じサイズを占めることです。
これは、データメンバの初期化や値の割り当てをより速く行うためのアイデアです。つまり、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