C 言語でのビットマスキング
この記事では、C 言語でビットマスクを使用する方法について複数の方法を示します。
キーワード struct
を使って C 言語でビットマスクデータを定義する
ビットマスクは通常、ビットフィールド形式のデータ構造の個々のセクションにアクセスしたり設定したりするためのビット演算に使用されます。一方、ビットフィールドは、データを効率的に格納し、メモリフットプリントを削減するために利用されます。また、ビットワイズ演算は、一般的な算術演算よりもハードウェアで実行する方が比較的高速です。以下の例では、struct
キーワードを用いてビットフィールドの実装を示します。
これは、従来のメンバアクセス演算子を使用して、与えられたビット領域を取得できるオブジェクトを構築するための特別な記法であることに注意してください。Bitfield
構造体は 32 ビットを占める単一の unsigned
整数をメモリに格納しますが、23 ビット、5 ビット、4 ビットの 3つの異なるサイズのセクションにアクセスすることもできます - それぞれ year
、day
、month
と名付けられた値です。結果として、Bitfield
はメモリ内で効率的に実装された日付の抽象化を表しています。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
struct {
uint32_t year : 23;
uint32_t day : 5;
uint32_t month : 4;
} typedef Bitfield;
int main() {
Bitfield date = {2020, 13, 12};
printf("sizeof Bitfield: %lu bytes\n", sizeof(date));
printf("date: %d/%d/%d \n", date.day, date.month, date.year);
return EXIT_SUCCESS;
}
出力:
sizeof Bitfield: 4 bytes
date: 13/12/2020
struct
を union
と組み合わせて C でビットマスクデータを定義する
あるいは、前の構造体に union
キーワードを追加して、32 ビットの数値全体に別々にアクセスできるようにすることもできます。ビットフィールドのメンバへのアクセスは struct
のメンバへのアクセスよりも遅いので、別個の ydm
整数メンバにビット演算を用いて日付の値を割り当てます。
与えられた日付を表す 10 進数は論理的に互いに OR 演算されるが、その前に day
と month
の値をそれぞれ左に 23 桁、28 桁シフトすることに注意してください。後者の数値は、これらのメンバがビットフィールド内で占める対応するビット位置に基づいて決定されています。出力を出力するために必要な場合でも、各メンバは個別にアクセスできることに注意してください。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
union {
struct {
uint32_t year : 23;
uint32_t day : 5;
uint32_t month : 4;
};
uint32_t ydm;
} typedef BitfieldFast;
int main() {
BitfieldFast date_f;
date_f.ydm = 2020 | (13 << 23) | (12 << 28);
printf("sizeof BitfieldFast: %lu bytes\n", sizeof(date_f));
printf("date_f: %d/%d/%d \n", date_f.day, date_f.month, date_f.year);
return EXIT_SUCCESS;
}
出力:
sizeof BitfieldFast: 4 bytes
date_f: 13/12/2020
ビットマスクを使用するもう一つの典型的な例は、ネットワークにおける IP アドレスです。すなわち、IP アドレスにはネットワークマスクが付与され、これにより、与えられたアドレスがどのネットワークに属しているかが決定されます。ネットワークアドレスの計算は、IP アドレスとそのネットワークマスクを論理的に AND することで行われます。この場合、IP アドレスとネットマスクを別々に格納するために、struct
というビットフィールドを定義します。なお、論理和は 32 ビットの値全体に対して行われるが、8 ビットの 4つのセクションとして出力する場合は、メンバアクセス演算子を用いることに注意してください。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
union {
struct {
uint8_t first : 8;
uint8_t second : 8;
uint8_t third : 8;
uint8_t fourth : 8;
};
uint32_t ip;
} typedef IPAddress;
int main() {
IPAddress ip1 = {10, 127, 5, 1};
IPAddress mask = {255, 255, 240, 0};
printf("ip1: %d.%d.%d.%d\n", ip1.first, ip1.second, ip1.third, ip1.fourth);
ip1.ip = ip1.ip & mask.ip;
printf("net: %d.%d.%d.%d\n", ip1.first, ip1.second, ip1.third, ip1.fourth);
return EXIT_SUCCESS;
}
出力:
ip1: 10.127.5.1
net: 10.127.0.0