ここからはビット演算について説明します。
ビット演算とは、2進数を使った計算方法の1種です。
もしかすると「なぜ今頃?」と思われた方もいるかも知れません。
この講座は「プログラムが作れるようになる事」を主眼に置いています。
これまでのプログラムでは、論理演算は必要ありませんでしたので、説明を後回しにしました。
※多くのC言語のテキストなどでは、最初の方に書いてありますね。
※2進数を覚えていない方は「基数変換」などのコラムで再度確認してみてください。
論理演算には次の4種類があります。
日本語 | 英語 | 記号 | 備考 |
---|---|---|---|
論理積 | and | & | |
論理和 | or | | | shiftキーを押しながら¥ |
排他的論理和 | xor 又は eor | ^ | ¥の左 |
否定 | not | ~ | shiftキーを押しながら^ |
まずはそれぞれの考え方から説明しましょう。
論理積(and, &)
1と1をand演算すると1になります。 1と0をand演算すると0になります。 0と0をand演算すると0になります。
4ビットで表してみると、
<論理積> 1010 & 1100 ------ 1000
となります。
論理和(or, |)
1と1をor演算すると1になります。 1と0をor演算すると1になります。 0と0をor演算すると0になります。
4ビットで表してみると、
<論理和> 1010 | 1100 ------ 1110
となります。
排他的論理和(xor又はeor, ^)
1と1をxor演算すると0になります。 1と0をxor演算すると1になります。 0と0をxor演算すると0になります。
4ビットで表してみると、
<排他的論理和> 1010 ^ 1100 ------ 0110
となります。
否定だけは、単項演算(++や−−と同じ)です。
否定(not, 〜)
1をnot演算すると0になります。 0をnot演算すると1になります。
4ビットで表してみると、
<否定> ~ 1010 ------ 0101
となります。
C言語で論理演算を使用する際には、2進数が使えませんので16進数を使う事が多いです。
桁が多いと分かりづらいので、一番小さいサイズの型「char型:8ビット」を使って作ります。
論理演算の前に、まずは注意すべき点から説明します。
まずはこのプログラムの実行結果を考えてみてください。
<sample program 177-01>
#include <stdio.h> int main(void) { char data1 = 0xAF; printf("data1 = %X\n", data1); return 0; } |
<実行結果>
解答です。
data1 = FFFFFFAF 続行するには何かキーを押してください・・・
考えた結果と同じだったでしょうか?
char型は8ビットの変数のはずです。
0xAF
は、2進数で表すと、
1010 1111
だと、「基数変換」の最後で書きました。
しかし、結果を2進数で表すと、
1111 1111 1111 1111 1111 1111 1010 1111
8ビットではなく、32ビットで表示されました。
どうしてなのでしょうか?
とりあえず、10進数で表示してみましょう。
<sample program 177-02>
#include <stdio.h> int main(void) { char data1 = 0xAF; printf("data1 = %d\n", data1); return 0; } |
<実行結果>
data1 = -81 続行するには何かキーを押してください・・・
なぜか、マイナスの数値が表示されました。
2進数でのマイナスの表現は、「マイナスの表現」で書きました。
「2の補数」という考え方では、最上位ビット(2進数を横に書いて一番左のビット)は符号がマイナスということを表しています。
ですから、
0xAF
は、
1010 1111
最上位ビットが1なので、マイナスの値として認識されます。
細かく書くと↓の通りで、
1の位 が 1個 = 1 2の位 が 1個 = 2 4の位 が 1個 = 4 8の位 が 1個 = 8 16の位 が 0個 = 0 32の位 が 1個 = 32 64の位 が 0個 = 0 −128の位 が 1個 = −128
合計すると、「−81」です。
よく見るとプログラムをコンパイルした時に、警告が出ています。
warning C4309: '初期化中': 定数値が切り捨てられました。
直接的な意味は分かりづらいですが、何か問題がある事は分かります。
0xAF
を、マイナスの値ではなく、単純なビット列として扱うにはこれではダメということです。
マイナスの値として認識されているのは、char型が「符号あり」の型だからです。
「変数について」で説明しました。
整数型を使う際には、「符号あり」か「符号なし」かをプログラマが選べるようになっています。
ビット列を扱う際には、「符号なし」を選択することが重要です。
では、プログラムを書き換えてみましょう。
<sample program 177-03>
#include <stdio.h> int main(void) { unsigned char data1 = 0xAF; printf("data1 = %d\n", data1); return 0; } |
<実行結果>
data1 = 175 続行するには何かキーを押してください・・・
コンパイル時の警告も無くなっていますよね。
「符号なし」になりましたから、
1の位 が 1個 = 1 2の位 が 1個 = 2 4の位 が 1個 = 4 8の位 が 1個 = 8 16の位 が 0個 = 0 32の位 が 1個 = 32 64の位 が 0個 = 0 128の位 が 1個 = 128
合計すると、「175」です。
では、16進数で表示し直します。
<sample program 177-04>
#include <stdio.h> int main(void) { unsigned char data1 = 0xAF; printf("data1 = %X\n", data1); return 0; } |
<実行結果>
data1 = AF 続行するには何かキーを押してください・・・
やっと論理演算を使う準備が出来ました。
では、最後に論理積を使ってみましょう。
<sample program 177-05>
#include <stdio.h> int main(void) { unsigned char data1 = 0xAF; unsigned char data2 = 0xCC; unsigned char result = data1 & data2; printf("result = %X\n", result); return 0; } |
<実行結果>
data1 = 8C 続行するには何かキーを押してください・・・
2進数で書くと↓の通りです。
0xAF → 1010 1111 0xCC → 1100 1100 1010 1111 & 1100 1100 ----------- 1000 1100 1000 1100 → 0x8C
次回は、使い道を考えながら色々やってみましょう。