導入
ゲームの状態管理や通信のヘッダなど、「1つの整数の中に複数のON/OFFスイッチを詰め込みたい」場面があります。そのために使うのが、整数を2進数のビット単位で操作するビット演算子です。
説明
int や unsigned int はコンピュータの中では2進数(0と1の並び)として表現されています。例えば 12 は2進数で 1100、10 は 1010 です。ビット演算子は、この0/1の並びを1桁(1ビット)ずつ操作します。
&(AND) … 両方のビットが1のときだけ1になる。|(OR) … どちらかのビットが1なら1になる。^(XOR、排他的論理和) … 2つのビットが異なるときだけ1になる。~(NOT、単項演算子) … すべてのビットを反転する(0↔1)。<<(左シフト) … ビットを左にずらす。1回ずらすごとに値は2倍になる。>>(右シフト) … ビットを右にずらす。1回ずらすごとに値は半分(整数の割り算と同じ切り捨て)になる。
flowchart LR a["a = 1100 (12)"] --- op["& (AND)"] b["b = 1010 (10)"] --- op op --> r["結果 = 1000 (8)"]
#include <stdio.h>
int main(void) {
unsigned int a = 12; // 2進数: 1100
unsigned int b = 10; // 2進数: 1010
printf("a & b = %u\n", a & b); // 1000 = 8
printf("a | b = %u\n", a | b); // 1110 = 14
printf("a ^ b = %u\n", a ^ b); // 0110 = 6
printf("~a の下位8bit = %u\n", (~a) & 0xFF); // 11110011 = 243
printf("a << 2 = %u\n", a << 2); // 110000 = 48
printf("a >> 2 = %u\n", a >> 2); // 0011 = 3
return 0;
}
~a は a のすべてのビット(unsigned int なので通常32ビット分)を反転するので、そのまま表示すると非常に大きな数になります。上の例では & 0xFF で「下位8ビットだけ取り出す」マスク処理をしてから表示し、結果が分かりやすくなるようにしています。このように「他のビットに影響を与えず、注目したいビットだけを取り出す/変更する」ときに使う値をビットマスクと呼びます(次のレッスンで詳しく使います)。
やってみよう
a と b の値を変えて結果がどう変わるか確認しましょう。a << 1、a << 3 のようにシフトする回数を変えて、シフトが「2倍・4倍・8倍」に対応することを確かめてみてください。
演習
unsigned int x = 6; と unsigned int y = 3; を使って、x & y、x | y、x ^ y の3つの結果をそれぞれ1行ずつ表示してください。
ヒント1を見る
printf("%u\n", x & y); のように3回 printf します。
ヒント2を見る
6は2進数で 0110、3は 0011 です。ANDは両方1のビットだけ残ります。