【C言語】
ビット演算子の基本的な使い方
~よくある間違いの紹介~

■この記事の概要

この記事では、C言語でのビット演算子とマスクパターンのよくある誤りを解説しています。

特に、if文でのビット演算の結果が常に真や偽になるケースを紹介し、その原因を詳しく説明しています。

具体的なコード例も提供され、間違いを理解するための参考になります。


■ビット演算子の基本(AND)

#include <stdio.h>
int main(void){
    printf("0 & 0 = %x\n",0 & 0);
    printf("0 & 1 = %x\n",0 & 1);
    printf("1 & 0 = %x\n",1 & 0);
    printf("1 & 1 = %x\n",1 & 1);
}
./a.out
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

ビットごとのAND演算を行います。
両辺とも1ならば1になります。


■ビット演算子の基本(OR)

#include <stdio.h>
int main(void){
    printf("0 | 0 = %x\n",0 | 0);
    printf("0 | 1 = %x\n",0 | 1);
    printf("1 | 0 = %x\n",1 | 0);
    printf("1 | 1 = %x\n",1 | 1);
}
./a.out
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

ビットごとのOR演算を行います。
両辺のどちらかでも1ならば1になります。


■ビット演算の結果を2進数で表現する

#include <stdio.h>
void to_binary(int number) ;
int main(void){
    to_binary(
            0b1001
        &   0b1010  //AND
    );//    0b1000
    to_binary(
            0b1001
        |   0b1010  //OR
    );//    0b1011
}
void to_binary(int number) {
    // ビット数を固定4ビット整数と仮定)
    const int BITS = sizeof(char) * 4;
    char binary[BITS + 1];  // 終端文字('\0')のために+1
    binary[BITS] = '\0';    // 文字列終端
    for (int i = BITS - 1; i >= 0; i--) {
        binary[i] = (number & 1) ? '1' : '0'; // LSBからビット取得
        number >>= 1;                         // 右シフト
    }
    printf("2進数: %s\n", binary);
}
./a.out
2進数: 1000
2進数: 1011

C99仕様までのC言語のprintfでは2進数表記が出来ないので、自作する必要があります。


■|と&は間違えやすい

// |と&を間違える
#include <stdio.h>
int main(void){
    int x = 0;
    if(x | 1){
        puts("間違った書き方:常に成り立つ");
    }
    if(x & 1){
        puts("正しい書き方");
    }
}

プログラマと言えどもビット演算子は
四則演算より馴染みが少ないので、
|(OR)と&(AND)は間違いやすいです。


■&と==の結合力を間違える

//結合力の誤り
//warning: suggest parentheses around comparison in operand of ‘&’ 
//[-Wparentheses]
#include <stdio.h>
int main(void){
    int x = 2;
    if(x & 2 == 2){
        puts("間違った書き方:(2==2)で結合");
    }
    if((x & 2) == 2){
        puts("正しい書き方");
    }
}

==は&より結合力が強いので
if(x & 2 == 2){は
if(x & (2 == 2)){とコンパイラは解釈します。


■ビットパターンを間違える

//ビットパターンの不一致
#include <stdio.h>
int main(void){
    int x = 2 ;
    if((x & 2) == 1)    puts("NG");
    if((x & 2) == 2)    puts("OK");

    int y = 0b0010 ;
    if((y & 0b0010) == 0b0001)  puts("NG");
    if((y & 0b0010) == 0b0010)  puts("OK");
}

1(0b0001)と
2(0b0010)はビットパターンが重ならないので
if((x & 2) == 1)が成立する事はありません。


■ビット演算の結果をtrueと比較すると間違いやすい

//ビット演算の結果を true と比較する間違い
//warning: bitwise comparison always evaluates to false 
//[-Wtautological-compare]
#include <stdio.h>
#include <stdbool.h>
int main(void){
    int x = 2 ;
    if((x & 2) == true)    
        puts("間違い");
    if((x & 2) == 1)    
        puts("間違い");
    if((x & 2) == 2)    
        puts("良い");
    if(x & 2)    
        puts("とても良い:明示的に==2の比較は不要");
}

true/falseは論理演算で使う定数なので
ビット演算に使用すると間違いやすいです。

又、
if((x&mask)==mask)のパターンは
maskが2のべき乗の時、
==maskは省略できます。

0と1を間違えてビットオフ判定をする

//0と1を間違える
#include <stdio.h>
unsigned int bit ;
int main(void){
    bit &= ~(1<<7);//7bitを下げる
    
    if(bit & (0<<7)){
        puts("間違った書き方");
    }
    if((bit & (1<<7)) == 0){
        puts("正しい書き方");
    }
}

特定のビットが下がっているか判定する時
(ビットオフ判定する時)0と1を間違った例です。
if(bit & (0<<7))ではbitの値に関係なく常時偽です。


参考:

EXP00-C. 括弧を使用して演算の優先順位を指定する