HOWTO · C

C でビットを比較する方法

この記事では、C でビットを比較する方法を示します。

このページの内容

ビット演算はコンピュータプログラミングの基本であり、特に低レベルのデータ操作を行う場合に重要です。C 言語では、個々のビットやビットのグループを比較する方法がいくつかあります。

この記事では、各方法を示すコード例とともに、さまざまな手法を探ります。

C 言語でのビット比較のためにビット単位の XOR および AND 演算を使用してカスタム関数を実装する

ビット単位の AND 演算子(&)は、2 つのオペランドの対応するビット同士のビット単位の比較を行うために使用されます。この演算の結果は、両方のオペランドで対応するビットが設定されている場合にのみ各ビットが設定される新しい値になります。

サンプルコード:

#include <stdio.h>

int main() {
  unsigned int num1 = 9;  // Binary representation: 1001
  unsigned int num2 = 6;  // Binary representation: 0110

  unsigned int result = num1 & num2;

  printf("Result: %u\n", result);  // Output: 0 (Binary: 0000)

  return 0;
}

出力:

Result: 0

この例では、num1 と num2 がそれぞれ値 9 と 6 で初期化されています。これらの値は 2 進数で 1001 と 0110 と表されます。

num1 と num2 の間のビット単位の AND 演算(num1 & num2)の結果は 2 進数で 0000 となり、10 進数では 0 です。これは、num1 と num2 の両方で設定されている対応ビットがないことを意味します。

一方、ビット単位の XOR 演算子(^)は、2 つのオペランドの対応するビット同士のビット単位の比較を行うために使用されます。この演算の結果は、オペランドの対応するビットが異なる場合に各ビットが設定される新しい値になります。

サンプルコード:

#include <stdio.h>

int main() {
  unsigned int num1 = 9;  // Binary representation: 1001
  unsigned int num2 = 6;  // Binary representation: 0110

  unsigned int result = num1 ^ num2;

  printf("Result: %u\n", result);  // Output: 9 (Binary: 1001)

  return 0;
}

出力:

Result: 15

この例では、ビット単位の XOR 演算(num1 ^ num2)の結果は 2 進数で 1001 となり、10 進数では 9 です。これは、num1 と num2 の対応するビットが異なることを意味します。

一般に、ビット比較では、単一ビットの値にアクセスし、union および struct キーワードを使用してビットフィールドを実装するなど、必要な操作を実行する必要があります。

ただし、ビット演算は、数値内の指定されたビットを比較するためのより効率的な方法を提供します。この場合、32 ビット幅が保証されている u_int32_t 型に適した別の関数を実装します。

以下のサンプルプログラムは、コマンドライン引数として 3 つの整数を受け取ります。最初の 2 つは比較される数値であり、3 番目の整数は n 番目のビットを指定します。strtol を使用して argv 要素を変換するため、戻り値を u_int32_t 型に格納する際に精度が多少低下することに注意してください。

compareBits 関数は、中間値を格納する 2 つのローカル変数 mask と tmp を宣言します。1 を nth - 1 の位置だけ左にシフトすることにより、mask の n 番目のビットを設定します。

次に、両方のユーザー入力数値に XOR を適用して各位置のビット差を取得し、結果にビットが設定されている場合は値が異なることを示します。最後に、n 番目の位置からビットを抽出し、値が 0 であるかどうかを確認する必要があります。

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

bool compareBits(u_int32_t n1, u_int32_t n2, u_int32_t nth) {
  u_int32_t mask, tmp;

  mask = 1 << (nth - 1);
  tmp = n1 ^ n2;

  if ((tmp & mask) == 0)
    return true;
  else
    return false;
}

int main(int argc, char *argv[]) {
  u_int32_t num1, num2, bit;

  if (argc != 4) {
    fprintf(stderr, "Usage: %s integer1 integer2 nth_bit \n", argv[0]);
    exit(EXIT_FAILURE);
  }

  num1 = strtol(argv[1], NULL, 0);
  num2 = strtol(argv[2], NULL, 0);
  bit = strtol(argv[3], NULL, 0);

  compareBits(num1, num2, bit) ? printf("bits equal!\n")
                               : printf("bits not equal!\n");

  exit(EXIT_SUCCESS);
}

サンプルファイル形式:

./main 1234 1231 1

入力ファイル形式:

bits not equal!

例として、整数 1234 と 1231 は 2 進数でそれぞれ 00000000000000000000010011010010 と 00000000000000000000010011001111 と表されます。

したがって、この 2 つに XOR を適用すると 2 進数表現 00000000000000000000000000011101 が得られ、最終的にマスク 00000000000000000000000000000010 と AND を適用して単一ビット位置の値を抽出します。結果がすべてゼロの場合、比較されたビットは等しいことを意味し、そうでない場合はその逆です。

ただし、別の方法として、次のサンプルコードに示すように、compareBits 関数を短縮し、XOR/AND 演算を return 文に移動することもできます。この関数はビット単位の XOR/AND 演算の論理的な反対を返すことに注意してください。これは、呼び出し側の関数で ? : 三項演算子を使用して対応するメッセージを出力するためです。

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

bool compareBits(u_int32_t n1, u_int32_t n2, u_int32_t nth) {
  u_int32_t mask;

  mask = 1 << (nth - 1);

  return !((n1 ^ n2) & mask);
}

int main(int argc, char *argv[]) {
  u_int32_t num1, num2, bit;

  if (argc != 4) {
    fprintf(stderr, "Usage: %s integer1 integer2 nth_bit \n", argv[0]);
    exit(EXIT_FAILURE);
  }

  num1 = strtol(argv[1], NULL, 0);
  num2 = strtol(argv[2], NULL, 0);
  bit = strtol(argv[3], NULL, 0);

  compareBits(num1, num2, bit) ? printf("bits equal!\n")
                               : printf("bits not equal!\n");

  exit(EXIT_SUCCESS);
}

サンプルファイル形式:

./main 1234 1231 2

入力ファイル形式:

bits equal!

C 言語でのビット比較にビット単位の OR(|)演算子を使用する

ビット単位の OR 演算子(|)は、2 つのオペランドの対応するビット同士のビット単位の比較を行います。この演算の結果は、いずれかのオペランドで対応するビットの少なくとも 1 つが設定されている場合に各ビットが設定される新しい値になります。

ビットのペアに適用した場合、OR 演算は次のロジックに従います:

`0 0`
`0 1`
`1 0`
`1 1`

ビット単位の OR 演算を説明するために、次の例を考えてみましょう:

#include <stdio.h>

int main() {
  unsigned int num1 = 9;  // Binary representation: 1001
  unsigned int num2 = 6;  // Binary representation: 0110

  unsigned int result = num1 | num2;

  printf("Result: %u\n", result);  // Output: 15 (Binary: 1111)

  return 0;
}

出力:

Result: 15

この例では、値 9 と 6 でそれぞれ初期化された 2 つの符号なし整数 num1 と num2 があります。それらの 2 進数表現は 1001 と 0110 です。

ビット単位の OR 演算(num1 | num2)を実行すると、結果は 2 進数で 1111 となり、10 進数では 15 です。これは、num1 または num2 のいずれかで対応するビットの少なくとも 1 つが設定されていることを意味します。

C 言語でのビット比較にビット単位の NOT(~)演算子を使用する

ビット単位の NOT 演算子(~)は単項演算子であり、単一のオペランドに対して動作することを意味します。2 進数に適用すると、各ビットが反転し、0 が 1 に、1 が 0 になります。

サンプルコード:

#include <stdio.h>

int main() {
  unsigned int num = 9;  // Binary representation: 1001

  unsigned int result = ~num;

  printf(
      "Result: %u\n",
      result);  // Output: 4294967286 (Binary: 11111111111111111111111111111010)

  return 0;
}

出力:

Result: 4294967286

この例では、符号なし整数変数 num を宣言し、値 9 で初期化します。2 進数では、これは 1001 と表されます。

次に、ビット単位の NOT 演算子(~num)を適用します。これによりすべてのビットが反転し、2 進数で 11111111111111111111111111111010 になります。

結果がコンソールに出力され、10 進数では 4294967286 です。

ビットを比較するためのビット単位の NOT

ビット単位の NOT 演算子の用途の 1 つは、ビットの比較です。変数のビットを反転することで、特定のビットが設定されているかどうかを簡単に確認できます。

サンプルコード:

#include <stdio.h>

int main() {
  unsigned int num = 9;  // Binary representation: 1001

  // Check if the 2nd bit is set
  unsigned int mask = 1 << 1;  // Shift 1 by 1 position to the left => 0002

  unsigned int result = ~num & mask;

  if (result == mask) {
    printf("2nd bit is set.\n");
  } else {
    printf("2nd bit is not set.\n");
  }

  return 0;
}

出力:

2nd bit is set.

この例では、変数 num の 2 番目のビットが設定されているかどうかを確認したいと思います。

まず、1 を 1 位置左にシフト(1 << 1)してマスクを作成し、2 進数で 0002 を得ます。次に、ビット単位の NOT 演算子を num に適用し、2 進数で 11111111111111111111111111111010 を得ます。

次に、反転した num とマスクの間でビット単位の AND 演算を実行します。これにより 2 番目のビットが分離されます。

結果がマスクと等しい場合、2 番目のビットが設定されていることを意味し、対応するメッセージを出力します。そうでない場合は、2 番目のビットが設定されていないことを示すメッセージを出力します。

結論

C 言語のビット演算子に関するこの包括的な探求では、AND、OR、NOT などの基本的な演算を取り上げました。これらの演算は、データ圧縮、暗号化、組み込みシステムプログラミングなど、低レベルのデータ操作を伴うタスクに非常に役立ちます。

特に、個々のビットを比較するためのビット単位の NOT(~)演算子の使用について詳しく掘り下げました。~ 演算子を適用することで、変数のビットを簡単に反転でき、特定のビットが設定されているかどうかを確認できます。この手法は、正確なビットレベルの操作が必要なシナリオで不可欠です。

ビット演算を理解し習得することは、ハードウェアとの直接的なやり取りを伴うタスクに取り組むプログラマや、リソースに制約のある環境向けの最適化されたソリューションを必要とするプログラマにとって重要です。この記事で得た知識により、これらの強力なツールを独自の C プログラミングプロジェクトで活用できるようになりました。