C 言語でのビットマスキング

胡金庫 2023年10月12日
  1. キーワード struct を使って C 言語でビットマスクデータを定義する
  2. structunion と組み合わせて C でビットマスクデータを定義する
C 言語でのビットマスキング

この記事では、C 言語でビットマスクを使用する方法について複数の方法を示します。

キーワード struct を使って C 言語でビットマスクデータを定義する

ビットマスクは通常、ビットフィールド形式のデータ構造の個々のセクションにアクセスしたり設定したりするためのビット演算に使用されます。一方、ビットフィールドは、データを効率的に格納し、メモリフットプリントを削減するために利用されます。また、ビットワイズ演算は、一般的な算術演算よりもハードウェアで実行する方が比較的高速です。以下の例では、struct キーワードを用いてビットフィールドの実装を示します。

これは、従来のメンバアクセス演算子を使用して、与えられたビット領域を取得できるオブジェクトを構築するための特別な記法であることに注意してください。Bitfield 構造体は 32 ビットを占める単一の unsigned 整数をメモリに格納しますが、23 ビット、5 ビット、4 ビットの 3つの異なるサイズのセクションにアクセスすることもできます - それぞれ yeardaymonth と名付けられた値です。結果として、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

structunion と組み合わせて C でビットマスクデータを定義する

あるいは、前の構造体に union キーワードを追加して、32 ビットの数値全体に別々にアクセスできるようにすることもできます。ビットフィールドのメンバへのアクセスは struct のメンバへのアクセスよりも遅いので、別個の ydm 整数メンバにビット演算を用いて日付の値を割り当てます。

与えられた日付を表す 10 進数は論理的に互いに OR 演算されるが、その前に daymonth の値をそれぞれ左に 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
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

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

LinkedIn Facebook