C言語のシフト演算とビット演算

概要
1. シフト演算について
1-1. 左シフト
1-2. 右シフト
2. ビット演算について
2-1. ANDの使用方法
2-2. ORの使用方法
2-3. XORの使用方法

1. シフト演算について

シフトは左シフト・右シフトがあり、CPUによってはローテートシフト(シフトして溢れたビットが反対側に現れる)も可能なものが存在します。

しかしC言語で「>>」や「<<」を使えば、ローテートではなく通常のシフトが使われるのが一般的です。

1-1. 左シフト

符号なし変数に対し、左へ1ビットシフトする事は、
値を2倍にすることに等しい。

[符号なし変数]
0001b << 1 (1) ---> 0010b (2)
0010b << 1 (2) ---> 0100b (4)
0100b << 1 (4) ---> 1000b (8)

符号付変数に対し、左へ1ビットシフトする時は、
その範囲に注意しなければなりません。

[符号付変数]
0001b << 1 (1) ---> 0010b (2)
0010b << 1 (2) ---> 0100b (4)
0100b << 1 (4) ---> 1000b (-8) *想定外の値になる可能性あり

1-2. 右シフト

符号なし変数に対し、右へ1ビットシフトする事は、
値を1/2にすることに等しい。

[符号なし変数]
1000b >> 1 ---> 0100b (4)
0100b >> 1 ---> 0010b (2)
0010b >> 1 ---> 0001b (1)

符号付変数に対し、右へ1ビットシフトする時は、
最上位ビット(MSB)の値に注意しなければなりません。

[符号付変数]
1000b >> 1 (-8) ---> 0100b (4) *想定外の値になる可能性あり
0100b >> 1 (4)  ---> 0010b (2)
0010b >> 1 (2)  ---> 0001b (1)

しかしC言語上で -8 >> 1と書いた場合、結果は-4になります。
これは算術シフトにより2の補数フォーマットが崩れないような形でシフトを行ってくれるからです。
具体的には次のようなシフトが行われます。

[符号付変数]
1000b >> 1 (-8) ---> 1100b (-4)
1100b >> 1 (-4) ---> 1110b (-2)
1110b >> 1 (-2) ---> 1111b (-1)

0111b >> 1 (+7) ---> 0011b (+3)
0011b >> 1 (+3) ---> 0001b (+1)

算術シフトは、シフト後もMSBビットが変化しないことに注意して下さい。


2. ビット演算について

注1 : 1ビット目を最下位ビットとして数えています
注2 : AND(ビット積)、OR(ビット和)、XOR(ビット差)

2-1. ANDの使用方法

ANDはビットのマスク処理によく使われます
例1として4ビットの中の特定ビットだけを取り出したい場合を考えます。

例1
元のデータ:1001b

この中の最下位ビット(LSB)だけを取り出したい場合、
0001bとのANDを行うことで取り出すことができます。

   1001b
 & 0001b
 --------------
   0001b

上位3ビットの内容は、0とのANDを行うことで、
必ず0になります。結果的にLSBビットだけを取り出すことができます。

例2
元のデータ:1100b

この中の3ビット目だけを取り出したい場合
0100bとのANDを行うことで取り出すことができます。

   1100b
 & 0100b
 --------------
   0100b

例1では、取り出した結果値は0か1になりますが、
例2では0か4になることに注意して下さい。

つまり、取り出したビットが1であった場合、結果は0以外の値になりますので、
if等で判定させる場合、それに合わせる必要があります。

2-2. ORの使用方法

ORは特定ビットを1にする為に使用されます

例1
元のデータ:1001b

この中の2ビット目を1にしたい場合

   1001b
 | 0010b
 --------------
   1011b

のように使用します。用途としては、偶数を奇数にしたり、
特定ビットを立てなければ書き込めないポートへの書き込みに使用されます。

例2 (偶数から奇数変換)

   1010b (10)
 | 0001b
 -------------
   1011b (11)

最下位ビットが1になることで、常に奇数にすることでできます。
2で割り切れたら1を足すというロジックでも出来ますが、それよりは遥かに高速です。

例3 (ポート出力)

あるポートに4ビットデータを書き込みたいが、上位4ビットが1でないと
書き込めないようにプロテクトされている場合、

   0000 0101 (元のデータ)
 | 1111 0000
 --------------------------
   1111 0101 (書き込みデータ)

上のように上位4ビットが常に1になり、書き込み条件を作ることができます。
これは組み込みでは重要なポートやレジスタに書き込む場合によく見られるようです。

2-3. XORの使用方法

XORはビット反転させたい場合やゼロクリアに使用されます

例1 (反転)

元のデータ:1001b

   1001b
 ^ 1111b
 ------------------
   0110b

例2 (クリア)

元のデータ:1001b

   1001b
 ^ 1001b
 ------------------
   0000b

上のように同じデータ同士でXORを取れば、必ずゼロになります。
CPUによっては、ゼロ代入より高速であることから好んで使われたり、
コンパイラが最適化により自動変換したりするようです。

以上になります。

参考:
ビット演算 - Wikipedia
職業としてのプログラミング ビットシフトの落とし穴 - 算術シフトと論理シフト