ビット演算は、整数値を ビット(0と1)単位で操作する演算である。効率的な計算やデータの操作が可能になる。2級では、各演算子の動作・シフトと2倍/半分の関係・符号付き右シフトの挙動・ビットマスクが問われる。
&(AND) |(OR) ^(XOR) ~(NOT) の動作が分かる ② 左シフト <<=×2/右シフト >>=÷2 を説明できる ③ 符号付き右シフトは符号ビットを維持・符号なしは0挿入 を区別できる ④ ビットマスクで特定ビットを取得(&)・設定(|)できる。
整数を2進数とみなし、ビットごとに演算する。基本は次の4つ。
| 演算子 | 名称 | 動作概要 | 例(2進数) |
|---|---|---|---|
& | ビットごとの論理積(AND) | 両方のビットが1のときだけ1、それ以外は0 | 1101 & 1011 = 1001 |
| | ビットごとの論理和(OR) | 少なくとも1つのビットが1なら1 | 1101 | 1011 = 1111 |
^ | 排他的論理和(XOR) | 2つのビットが異なるとき1(同じなら0) | 1101 ^ 1011 = 0110 |
~ | ビットの反転(NOT) | 各ビットを反転(0→1、1→0) | ~1101 = 0010 |
& | は、論理演算子 && ||(条件式の AND/OR)とは別物。ビット演算子は1ビットずつ、論理演算子は「真偽(0かそれ以外か)」全体を見る。
1101 & 1011 を桁ごとにそろえて計算すると、両方が1の桁だけ1になる。
1 1 0 1
&) 1 0 1 1
---------
1 0 0 1 ← 両方1の桁だけ1
シフト演算はビットの並びを左右にずらす演算である。
| 演算子 | 動作 | 意味 |
|---|---|---|
x << n | ビットを左に n 個ずらし、右側に0を入れる | ×2n(1回で2倍) |
x >> n | ビットを右に n 個ずらす | ÷2n(1回で半分) |
x = 5(2進数 0000 0101)をシフトする。
5 << 1 : 0000 0101 → 0000 1010 (10進数 10 = 5×2)
5 << 2 : 0000 0101 → 0001 0100 (10進数 20 = 5×4)
20(2進数 0001 0100)をシフトする。
20 >> 1 : 0001 0100 → 0000 1010 (10進数 10 = 20÷2)
20 >> 2 : 0001 0100 → 0000 0101 (10進数 5 = 20÷4)
x << 3 なら ×8(2の3乗)。乗算・除算をシフトで高速に置き換えられる、というのがビットシフトの旨み。
同じ「2倍・4倍」は、ふつうの算術でもシフトでも書ける。
int x;
int result1;
int result2;
int result3;
x = 5;
result1 = x * 2; /* 通常の算術:2倍 → 10 */
result2 = x * 4; /* 通常の算術:4倍 → 20 */
result1 = x << 1; /* シフト:2倍 → 10(0000 0101 → 0000 1010) */
result2 = x << 2; /* シフト:4倍 → 20 */
result3 = x >> 1; /* シフト:1/2 → 2(端数は切り捨て) */
5 >> 1 は 0000 0101 → 0000 0010 = 2(2.5 ではなく切り捨て)。
右シフトは「左側に何を入れるか」が型で変わる。ここが2級頻出。
unsigned):左側に必ず 0 を入れる。signed):右シフトで 符号ビット(一番左のビット)を維持する(処理系の多くがこの「算術シフト」)。負の数は左側に1が入る。負の数は2の補数で表される。-8 は8ビットで 1111 1000。これを右シフトすると:
-8 >> 1 : 1111 1000 → 1111 1100 (10進数 -4)
↑
左側に符号ビット 1 が入る(符号が保たれる)
-8 >> 1 は -4(負のまま、値は半分)。もし左側に0を入れてしまうと正の大きな数になってしまう。符号なしなら常に0挿入。
5 << 32 は無効。「ビット数を超えるシフトはしない」と覚える。
ビット演算は、整数の「特定の位置のビットだけ」を取り出したり立てたりするのに便利。これに使う値を マスク と呼ぶ。
&(AND)取り出したい桁だけ1にしたマスクと & を取ると、その桁だけが残る。
unsigned short x;
unsigned short mask;
unsigned short result;
x = 0x5A; /* 0101 1010 */
mask = 0x0F; /* 0000 1111 ← 下位4ビットだけ1 */
result = x & mask; /* 下位4ビットを取得 */
0101 1010 (x = 0x5A)
&) 0000 1111 (mask = 0x0F)
-----------
0000 1010 (result = 0x0A = 10進数 10)
上位4ビットは 0 & … で消え、下位4ビットだけが残る。
|(OR)立てたい桁だけ1にしたマスクと | を取ると、その桁が必ず1になる(他の桁は変わらない)。
unsigned short x;
unsigned short mask;
unsigned short result;
x = 0x5A; /* 0101 1010 */
mask = 0x01; /* 0000 0001 ← 最下位ビットだけ1 */
result = x | mask; /* 最下位ビットを1に設定 */
0101 1010 (x = 0x5A)
|) 0000 0001 (mask = 0x01)
-----------
0101 1011 (result = 0x5B = 10進数 91)
&、設定(1を立てる)は |。ついでに「反転(トグル)は ^」もセットで覚えると強い。
&(AND)・|(OR)・^(XOR)・~(NOT) の4つ。1ビットずつ計算する。<< = ×2(右側に0挿入)、右シフト >> = ÷2。乗除をシフトで高速化できる。-8 >> 1 は -4。&、設定は |。型のビット幅を超えるシフトはしない。