ビット演算子

C言語のビット演算子について解説します。ビット演算とは、1ビット単位で行われる演算のことをいいます。

ビットAND演算子&
ビットOR演算子|
ビットXOR演算子^
ビットNOT演算子~

ビットAND演算子

ビットAND演算子「&」は、二つのオペランドに含まれるあるビットが両方とも1の場合に、返却する値の対応する位置に1に設定し、あるビットのどちらかが0の場合は0を設定します。

# ビットAND
x & y

xとyが両方とも8bit符号なし整数型であるとしましょう。zがビットAND演算子の結果の値です。xとyのある位置の両方のビットが1の場合に、対応するzのビットも1になり、それ以外の場合は0になります。

x 01001100
y 01000101

z 01000100

ビットAND演算子のサンプルです。

#include <stdio.h>
#include <stdint.h>

int main(void) {
  // 01001100
  uint8_t x = 0x4C;
  
  // 01000101
  uint8_t y = 0x45;
  
  // 01000100
  uint8_t z = x & y;
  
  printf("%X\n", z);
}

出力結果です。16進数での出力ですが、2進数であれば「01000100」です。

44

ビットOR演算子

ビットOR演算子「|」は、二つのオペランドに含まれるあるビットのどちらかが1(どちらも1でもよい)の場合に、返却する値の対応する位置に1に設定し、あるビットの両方が0の場合は0を設定します。

# ビットOR
x | y

xとyが両方とも8bit符号なし整数型であるとしましょう。zがビットOR演算子の結果の値です。xとyのある位置のどちらかのビットが1の場合に、対応するzのビットも1になり、両方とも0の場合は0になります。

x 01001100
y 01000101

z 01001101

ビットOR演算子のサンプルです。

#include <stdio.h>
#include <stdint.h>

int main(void) {
  // 01001100
  uint8_t x = 0x4C;
  
  // 01000101
  uint8_t y = 0x45;
  
  // 01001101
  uint8_t z = x | y;
  
  printf("%X\n", z);
}

出力結果です。16進数での出力ですが、2進数であれば「01001101」です。

4D

ビットXOR演算子

ビットXOR演算子「^」は、二つのオペランドに含まれるあるビットがのどちらかだけが1の場合に、返却する値の対応する位置に1に設定し、あるビットの両方が0あるいは1の場合は0を設定します。

# ビットXOR
x ^ y

xとyが両方とも8bit符号なし整数型であるとしましょう。zがビットXOR演算子の結果の値です。xとyのある位置のどちらかだけのビットが1の場合に、対応するzのビットも1になり、両方とも0または1の場合は0になります。

x 01001100
y 01000101

z 00001001

ビットXOR演算子のサンプルです。

#include <stdio.h>
#include <stdint.h>

int main(void) {
  // 01001100
  uint8_t x = 0x4C;
  
  // 01000101
  uint8_t y = 0x45;
  
  // 0001001
  uint8_t z = x ^ y;
  
  printf("%X\n", z);
}

出力結果です。16進数での出力ですが、2進数であれば「0001001」です。

9

ビットNOT演算子

ビットNOT演算子「~」は、オペランドに含まれるあるビットを反転させます。

# ビットNOT
~x

xとyが両方とも8bit符号なし整数型であるとしましょう。zがビットNOT演算子の結果の値です。xのビットが反転しています。

x 01001100

z 10110011

ビットNOT演算子のサンプルです。出力結果は、整数型拡張の影響をなくすために「0xFF」とのビットANDをとっています。

#include <stdio.h>
#include <stdint.h>

int main(void) {
  // 01001100
  uint8_t x = 0x4C;
  
  // 01000101
  uint8_t y = 0x45;
  
  // 10110011
  uint8_t z = ~x;
  
  printf("%X\n", z);
}

出力結果です。16進数での出力で、すが、2進数であれば「01000100」です。

B3

ビット演算子と「=」を組み合わせた特殊演算子

ビット演算子と「=」を組み合わせた特殊演算子があります。

# 「x = x & y」 と同じ意味
x &= y

# 「x = x | y」 と同じ意味
x |= y

# 「x = x ^ y」 と同じ意味
x ^= y

ビット演算子は実務ではどのような場合に使用しますか?

ビット演算子を一番使う場面は、フラグを関数の引数に渡す場合です。C言語では、フラグを、整数で渡すことが多いのですが、その場合に、ビットの位置で意味を定めます。列挙型enumを使って、各ビットのフラグを定義します。1(二進で1), 2(二進で10), 4(二進で100), 8(二進で1000)。

各ビットを立てるときはビットOR「|=」、落とすときはビットXOR「^=」です。

ビットが立っているかをチェックするときは「&」を使います。

関数の引数に渡すフラグを作成するサンプルコードです。

#include <stdio.h>
#include <stdint.h>

enum {
  MYAPP_FLAG1 = 1,
  MYAPP_FLAG2 = 2,
  MYAPP_FLAG3 = 4,
  MYAPP_FLAG4 = 8,
  MYAPP_FLAG5 = 16,
  MYAPP_FLAG6 = 32,
  MYAPP_FLAG7 = 64,
  MYAPP_FLAG8 = 128,
  MYAPP_FLAG9 = 256,
  MYAPP_FLAG10 = 512,
};

int main(void) {
  uint32_t flag = 0;
  
  // MYAPP_FLAG4をたてる
  flag |= MYAPP_FLAG4;
  
  // 00000000 00000000 00000000 00001000
  printf("%X\n", flag);
  
  // MYAPP_FLAG10をたてる
  flag |= MYAPP_FLAG10;

  // 00000000 00000000 00000010 00001000
  printf("%X\n", flag);
  
  // MyAPP_FLAG4を落とす
  flag ^= MYAPP_FLAG4;
  
  // 00000000 00000000 00000010 00000000
  printf("%X\n", flag);
  
  // ビットが立っているかチェック
  if (flag & MYAPP_FLAG10) {
    printf("MYAPP_FLAG10 ok\n");
  }
}

関連情報