では、論理演算の使い道を考えながら続きを説明します。
今回は「フラグ」について書きましょう。
これまでフラグを使ったプログラムをいくつか書いてきました。
フラグにはint型の変数を使っていましたが、2進数で見るとあることに気が付きます。
int型は(私の環境では)32ビットです。
int flag = 0; |
として、変数flagを2進数にすると、
0000 0000 0000 0000 0000 0000 0000 0000
となります。
フラグは「0」と「1」で色々な状態を表すために使われますが、2進数で見ると最下位ビットしか使っていません。
0000 0000 0000 0000 0000 0000 0000 0000
残りの31ビットは全く使っていないのです。
メモリの容量がシビアな環境だと、非常に無駄な事だと言えます。
残りの31ビットも「0」と「1」が表現できるのですから、int型変数1つで32個のフラグが使えるのと同じことです。
そこで、ビット単位で演算が出来る論理演算の出番です。
32ビットは長すぎるので、8ビットで考えます。
とりあえず、↓のプログラムを作ってください。
<sample program 178-01>
#include <stdio.h> int main(void) { unsigned char flag = 0x00; printf("flag = %02X\n", flag); return 0; } |
<実行結果>
flag = 00 続行するには何かキーを押してください・・・
%02X
とは、
16進数2桁で表示する 1桁の場合は前に0を付ける
という意味になります。
現在のflagの中身を2進数で表現すると、
0000 0000
となります。
これを8個のフラグと見立てます。
右から0番フラグ、1番フラグと数えることにしますので、
7654 3210 番 0000 0000
となります。
ここで、3番フラグを1にするにはどうすれば良いでしょうか?
3番フラグの場所は、10進数で言うと「8」の位に当たります。
そこで、次のようなプログラムを組むと3番フラグが1になります。
<sample program 178-02>
#include <stdio.h> int main(void) { unsigned char flag = 0x00; flag = 8; printf("flag = %02X\n", flag); return 0; } |
<実行結果>
flag = 08 続行するには何かキーを押してください・・・
16進数の「08」は2進数で、
0000 1000
ですから、3番フラグが1になりました。
しかし、これでは分かりづらいですよね。
7番フラグを1にしようとすると、
flag = 128 |
と書かなければなりません。
しかも、1ビットごとにフラグとして扱うため、3番フラグが1の時に7番フラグも1にしようとすると、次のようなプログラムになります。
<sample program 178-03>
#include <stdio.h> int main(void) { unsigned char flag = 0x00; flag = 8; flag += 128; printf("flag = %02X\n", flag); return 0; } |
<実行結果>
flag = 88 続行するには何かキーを押してください・・・
16進数の「88」は2進数で、
1000 1000
ですから、7番フラグも1になりました。
この考え方は、論理演算を使うともう少し楽になります。
今回使う論理演算は「論理和:or演算」です。
前回説明したとおり「論理和」は、
1と1をor演算すると1になります。 1と0をor演算すると1になります。 0と0をor演算すると0になります。
という性質を持っています。
0のビットに1をor演算すると、1に変える事が出来ます。
実際にプログラムにしてみましょう。
フラグが0の状態から、3番フラグを1にします。
<sample program 178-04>
#include <stdio.h> int main(void) { unsigned char flag = 0x00; unsigned char mask = 0x08; flag = flag | mask; printf("flag = %02X\n", flag); return 0; } |
<実行結果>
flag = 08 続行するには何かキーを押してください・・・
論理演算を行うためのビット列を「マスク」と言います。
このマスクに、ビットを1にしたいところに1を入れたビット列を入れます。
0x08 → 0000 1000 (3番フラグを1にしたい)
フラグに対して、マスクをor演算することによって、その場所が1になります。
0000 0000 | 0000 1000 ----------- 0000 1000
続いて、7番フラグも1にしたい場合は次のようにします。
<sample program 178-05>
#include <stdio.h> int main(void) { unsigned char flag = 0x00; unsigned char mask = 0x08; flag = flag | mask; mask = 0x80; flag = flag | mask; printf("flag = %02X\n", flag); return 0; } |
<実行結果>
flag = 88 続行するには何かキーを押してください・・・
マスクの内容を変えて、同じ演算をするだけです。
flag = flag | mask; |
は、
flag |= mask; |
と書く事も出来ますので、これからはこう書きましょう。
※ビットを1にすることを「ビットを立てる」という表現もあります。
フラグの該当ビットを1にする方法は分かりましたが、そのビットが1かどうかはどうやって調べるのでしょうか?
これは、「論理積:and演算」を使います。
「論理積」は、
1と1をor演算すると1になります。 1と0をor演算すると0になります。 0と0をor演算すると0になります。
という性質を持っています。
1と1の時だけ、結果が1になるのです。
調べたいビットに1を入れたマスクを作りand演算してみます。
結果は別の変数に入れて確認します。
<sample program 178-06>
#include <stdio.h> int main(void) { unsigned char flag = 0x46; unsigned char mask = 0x02; unsigned char result; result = flag & mask; printf("result = %02X\n", result); return 0; } |
<実行結果>
result = 02 続行するには何かキーを押してください・・・
0x46 → 0100 0110 0x02 → 0000 0010
1番フラグが1かどうか調べています。
0100 0110 & 0000 0010 ----------- 0000 0010
結果は2となりましが、これで何が分かるのでしょうか?
データを変えて、1番ビットが0のケースを確認してみます。
<sample program 178-07>
#include <stdio.h> int main(void) { unsigned char flag = 0x44; unsigned char mask = 0x02; unsigned char result; result = flag & mask; printf("result = %02X\n", result); return 0; } |
<実行結果>
result = 00 続行するには何かキーを押してください・・・
0x44 → 0100 0100 0x02 → 0000 0010
1番フラグを調べています。
0100 0100 & 0000 0010 ----------- 0000 0000
and演算はお互いが1の時しか1になりません。
結果を見ると「0」になっています。
演算した結果が0の場合は、該当フラグが1では無かったということです。
最後に、該当ビットを0にする方法を書きます。
これには、「排他的論理和:xor演算」を使います。
「排他的論理和」は、
1と1をor演算すると0になります。 1と0をor演算すると1になります。 0と0をor演算すると0になります。
という性質を持っています。
お互い異なる値の時だけ結果が1になり、同じ値の時は0になるのです。
これを使ってビットを0にしてみます。
<sample program 178-08>
#include <stdio.h> int main(void) { unsigned char flag = 0x67; unsigned char mask = 0x04; unsigned char result; result = flag ^ mask; printf("result = %02X\n", result); return 0; } |
<実行結果>
result = 63 続行するには何かキーを押してください・・・
0x67 → 0110 0111 0x04 → 0000 0100
2番フラグを0にしようとしています。
0110 0111 ^ 0000 0100 ----------- 0110 0011
どうでしょうか、ちゃんと2番フラグだけ0になりました。
後々説明するシフト演算と組み合わせるともっと楽に組めるようになります。