ビット演算子 & の優先順位が比較演算子 == より低いことによる誤動作
a & b == cと書くと、==の方が優先順位が高いため、a & (b == c)として解釈されます。意図した(a & b) == cとは異なる結果になります。
なぜエラーが出ないのか
出力: no
(エラーなし)- Cは何も報告しません。コンパイルも実行も正常に完了するためです
出力: no- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 #include <stdio.h> 2 3 int main(void) { 4 int flags = 6; 5 int mask = 2; 6 if (flags & mask == mask ) { ^ 7 printf("yes\n"); 8 } else { 9 printf("no\n"); 10 } 11 return 0; 12 }
==は&よりも優先順位が高いため、flags & mask == maskはflags & (mask == mask)として解釈されます。ビット演算と比較を混ぜるときは括弧で明示的にグループ化する必要があります。
直し方: flags & mask == mask を (flags & mask) == mask にします。
パターン2
1 #include <stdio.h> 2 3 int main(void) { 4 int bits = 12; 5 int test = 4; 6 if (bits & test == test ) { ^ 7 printf("yes\n"); 8 } else { 9 printf("no\n"); 10 } 11 return 0; 12 }
bits & test == testはbits & (test == test)、つまりbits & 1と解釈されます。12は偶数なので最下位ビットが0になり、常に0(偽)になってしまいます。
直し方: bits & test == test を (bits & test) == test にします。
パターン3
1 #include <stdio.h> 2 3 int main(void) { 4 int level = 8; 5 int flag = 8; 6 if (level & flag == flag ) { ^ 7 printf("yes\n"); 8 } else { 9 printf("no\n"); 10 } 11 return 0; 12 }
levelとflagが等しくても、優先順位のせいでlevel & (flag == flag)、つまりlevel & 1が計算されてしまい、期待した比較にはなりません。
直し方: level & flag == flag を (level & flag) == flag にします。
パターン4
1 #include <stdio.h> 2 3 int main(void) { 4 int perms = 20; 5 int need = 4; 6 if (perms & need == need ) { ^ 7 printf("yes\n"); 8 } else { 9 printf("no\n"); 10 } 11 return 0; 12 }
==は&より優先順位が高いため、括弧が無いとperms & (need == need)として解釈されます。
直し方: perms & need == need を (perms & need) == need にします。
パターン5
1 #include <stdio.h> 2 3 int main(void) { 4 int state = 18; 5 int check = 2; 6 if (state & check == check ) { ^ 7 printf("yes\n"); 8 } else { 9 printf("no\n"); 10 } 11 return 0; 12 }
ビット演算と比較を混ぜるときは、必ず括弧で優先順位を明示する必要があります。
直し方: state & check == check を (state & check) == check にします。
よくある誤解
算数の感覚では&も==も同じくらいの優先度に見えますが、Cの演算子優先順位表では比較演算子(==, !=, <, >など)はビット演算子(&, |, ^)よりも優先順位が高く設定されています。ビット演算と比較を混ぜるときは、必ず括弧で明示的にグループ化する必要があります。
実務での勘所
この優先順位はC言語の設計者Dennis Ritchie自身が後年、設計上の失敗だったと認めている有名な話です。Cの前身であるB言語から優先順位を引き継いだ際に、ビット演算と論理演算の優先順位の関係を見直す機会を逃してしまい、気づいたときにはすでに大量のコードがその優先順位に依存してしまっていたため、修正すると既存コードが壊れるという理由で現在まで直せずに残っています。歴史的な経緯で生まれた仕様であって、直感的に理解できるものではないと知っておくと納得しやすいです。