49章 ビット演算子

http://www.geocities.jp/ky_webid/c/049.html

ビットを扱うときに便利な演算子たちの愉快なお話です。

ビット単位の演算なので、2進数で考えます。しかし、C言語には2進数を表現する構文がないので、多くの場合16進数を使います。ただし、結局のところ内部では2進数な訳ですから、別に8進数や10進数で記述しても構いません。

しかしあれですね、ビットを扱うのが得意な言語の癖に2進数を表現する構文がないっていうのもちょっと考え物ですよね・・・。げふんげふん。

AND演算

ビットが1同士の時に1になるのがAND演算。

OR演算

どちらか一方が1であれば1になるのがOR演算。

NOT演算

ビットの反転を行うのがNOT演算。

XOR演算

共に同じビットの時に1になる(つまり違うビット同士なら0になる)のがXOR演算





ざっくり一行でまとめてみました。NOT演算なんて便利な物があるのなら前回の問題もっと楽に書けたのにね・・・。

ということで練習問題いってみましょう。

問題1

ある数Aとある数BをXOR演算し、その結果とBとを更にXOR演算するとどうなるか考えて下さい。そして、実際にプログラムを作って確かめて下さい。

脳内メモリが少ないので紙に書きながら考えるとしましょう。

例としてA「0101」とB「1100」と仮定します。

これをXORするとC「0110」になります。

このC「0110」とB「1100」をさらにXORするとD「0101」になります。このDは元のAと同じ値になってます。

つまりAとBをXORして更にBでXORすると、Aになるということですね。

実際にプログラムも書いてみます。

int main (int argc, char *argv[]) {
    int a = 10;
    int b = 30;

    printf("%d\n",(a ^ b) ^ b);
    return 0;
}
$ main
10
答え合わせ

正解ですね。

問題2

8ビットの数値の、最上位ビットが1かどうかを調べるプログラムを作って下さい。

上位一ビットということは10000000かどうかを調べれば良いということになりますね。

int main (int argc,char *argv[]) {
    int num;
    
    scanf("%d",&num);
    
    // 128は二進数で10000000
    printf("最上位ビットは %d です", (num & 128) ? 1 : 0);
    
    return 0;
}
$ main
1
最上位ビットは0です

$ main
127
最上位ビットは0です

$ main
128
最上位ビットは1です

$ main
129
最上位ビットは1です

うまく判定できてるっぽいですね。

答え合わせ

16進数で0x80は10000000とのこと。

また確認にchar型で使っていますね。確かにintだと最上位は符号ビットになってしまうのでchar型で確認した方がよさそうですね。ちなみに今回は8ビットという話だったのでintを使った判定でも問題はありませんでした。


問題3

AND演算子をうまく使うと、10進数を2進数に変換して画面に表示するプログラムが作れます。例えば100という10進数を、画面に1100100と出力するように、プログラムを作って下さい。

うまく使うと・・・。どううまく使えばいいんだ!それを考える問題ですね。

int main (int argc,char *argv[]) {
    int num = 100;
    int i;
    
    for(i=7;i>=0;--i) {
        if ( num & (int)pow(2,i) ) {
            printf("1");
        }
        else {
            printf("0");
        }
    }
    
    return 0;
}

とても難しいです。とりあえずごり押しな処理になってしまいました。

これは8ビット前提での変換なので4バイト分ちゃんと変換するのであればi=31にする必要があります。

また即printfしてるので微妙といえば微妙です。配列等に格納して後で出力するバージョンも作りました。

int main (int argc,char *argv[]) {
    int num = 100;
    int i;
    size_t len;
    char str[9];
    
    for(i=0;i<8;++i) {
        if ( num & (int)pow(2,i) ) {
            str[i] = '1';
        }
        else {
            str[i] = '0';
        }
    }
    str[i] = '\0';
    
    len = strlen(str) - 1;
    for(i=0;i<8;++i) {
        printf("%c",str[len-i]);
    }
    
    return 0;
}
答え合わせ

うわあ、凄いですね。

元の数値と同じか低い範囲でまず最上位ビットが1になる2進数を先にiに計算して格納しています。

そしてあとはiを2で割りながら計算すれば2進数の出来上がりです。

ちなみに2の累乗は2進数的に凄く便利ですね。以下表をあげます。

1  = 00000001
2  = 00000010
4  = 00000100
8  = 00001000
16 = 00010000
     :

といった感じにちょうど各ビットだけを対象に1となる数値になるわけです。2の累乗って凄い!便利!

2の累乗値と2進数が相性が良いってよく言われる所以はこういうことだったんですね。納得しました。