C言語基礎|論理演算子とビット演算子の違い

見た目が似てても中身は別モノ!&& と & を取り違えると、答えも動きもガラッと変わる。

論理演算子とビット演算子、ここでスッキリ分けよう

C言語には「似ている記号」がいくつかあります。中でも混同しやすいのが、

  • 論理演算子(&&、||、!)
  • ビット単位の論理演算子(&、|、~、^)

この2グループです。
パッと見は似てますが、扱う対象が違うので、結果も考え方も別になります。

  • 論理演算子:値を「真(0以外)/偽(0)」として判定し、結果は基本的に 0 または 1
  • ビット演算子:整数の各ビットを 0/1 とみなして演算し、結果はビット列そのもの

この記事では、表と図で「何が違うのか」を整理しながら、よくある事故(意図せず & を使った、短絡評価を期待していた等)も一緒に押さえます。

まず結論:何が違うの?

論理演算子とビット演算子の根本的な違い

観点論理演算子ビット演算子
対象真偽(0かそれ以外)ビット列(各ビット)
主な演算子&&、
判定基準0 が偽、0以外が真0 が偽、1 が真(各ビットごと)
結果0 または 1 になりやすいビット列の結果(0や1に限らない)
よく使う場面if の条件、ループ条件マスク処理、フラグ操作、ビット抽出
重要な特徴&& と

表の説明
この表は「同じAND/ORっぽい見た目でも、そもそも見ている世界が違う」という整理です。
論理演算子は “真偽の世界”、ビット演算子は “ビット列の世界” だと思うと混乱しにくいです。

それぞれの「書式」と「何をする命令か」

論理演算子(真偽を扱う)

演算子書式何をする命令?結果のイメージ
&&a && ba と b が両方真なら真真なら 1、偽なら 0
||a || ba と b のどちらかが真なら真真なら 1、偽なら 0
!!aa が偽なら真、真なら偽反転(0/1になりやすい)

ビット演算子(各ビットを扱う)

演算子書式何をする命令?結果のイメージ
&a & b各ビットでANDビット列が残る
|a | b各ビットでORビット列が立つ
^a ^ b各ビットでXORビット列が反転する場所が出る
~~a全ビット反転ビット列が反転した値

表の説明

  • && と & は「AND」という名前が同じでも、前者は真偽判定、後者はビット判定です。
  • || と | も同様です。
  • ! と ~ も “否定っぽい”けど、! は真偽の否定、~ は全ビット反転です。

具体例:5 & 4 と 5 && 4 はこう違う

提示文の要点を、図として見える形にします。

図:ビット演算 5 & 4(ビット列としてAND)

5  = 0101
4  = 0100
&  = 0100  → 4

図:論理演算 5 && 4(真偽としてAND)

5  は 0 ではない → 真
4  は 0 ではない → 真
真 && 真 → 真 → 1

図の説明

  • 5 & 4 は「0101 と 0100 の各ビットでAND」だから 0100 になって 4。
  • 5 && 4 は「どちらも非0だから真」なので 1。
    同じ “ANDっぽい” でも、結果が 4 と 1 に分かれるのが決定的な違いです。

事故が多いポイント:短絡評価の有無

論理演算子 && と || は、条件が決まった時点で 右側を評価しないことがあります(短絡評価)。
ビット演算子 & と | は、基本的に 両側を評価します。

短絡評価の違い

書式評価のしかたありがちな用途
a && ba が偽なら b を評価しないポインタがNULLでない時だけ次を確認
a || ba が真なら b を評価しない先に真が分かったら確定
a & b両方評価してからビットANDマスク処理
a | b両方評価してからビットORフラグの合成

表の説明
「安全確認のために右側を評価したくない」なら && や || が向いています。
ビット演算は “評価制御” が目的ではなく、ビット列の加工が目的です。

よく使う場面の違い(実務イメージ)

どう使い分ける?

やりたいこと使うのはどっち?
条件分岐したい論理演算子if (x > 0 && x < 10)
0かどうか調べたい論理演算子if (n != 0) / if (!n)
フラグを立てたい(ビットON)ビット演算子flags |= mask
フラグを消したい(ビットOFF)ビット演算子flags &= ~mask
特定ビットだけ取り出したいビット演算子x & 0x0F

表の説明

  • 条件式は “真偽” の世界なので論理演算子。
  • フラグやマスクは “ビット列” の世界なのでビット演算子。
    この切り替えができると、混同が一気に減ります。

サンプルプログラム

論理演算子とビット演算子の違いを確認するプログラムです。

プロジェクト名:chap7-20-1 ソースファイル名:chap7-20-1.c

// 論理演算子とビット演算子の違いを確認する
#include <stdio.h>

int main(void)
{
    int a = 6;   // 0110
    int b = 3;   // 0011

    printf("論理演算とビット演算の違いを確認します。\n");
    printf("a = %d, b = %d\n\n", a, b);

    printf("a & b  = %d  (ビットAND:各ビットでAND)\n", a & b);
    printf("a && b = %d  (論理AND:非0なら真として判定)\n\n", a && b);

    printf("a | b  = %d  (ビットOR:各ビットでOR)\n", a | b);
    printf("a || b = %d  (論理OR:どちらか非0なら真)\n\n", a || b);

    printf("!a     = %d  (論理否定:aが0なら1、非0なら0)\n", !a);
    printf("~a     = %d  (ビット反転:全ビットを反転)\n", ~a);

    return 0;
}

このプログラムで何が分かる?

  • a & b は 6(0110) と 3(0011) の共通する 1 のビットだけが残るので 2(0010) になります。
  • a && b は 6 も 3 も非0なので真 && 真 → 1 になります。
  • !a は真偽の反転、~a はビット列の反転なので、そもそも結果の意味が違います。

図で理解:6 と 3 の AND を2種類で比較

図:6 & 3(ビットとして計算)

6 = 0110
3 = 0011
& = 0010 → 2

図:6 && 3(真偽として計算)

6 は非0 → 真
3 は非0 → 真
真 && 真 → 1

図の説明
ここでも “2” と “1” に分かれます。
ビット演算は「ビット列の加工結果」、論理演算は「条件の成立(真偽)」が答えです。

つまずきやすいミニ注意

  • 条件式で & や | を使うと、意図せず両側が評価されてしまうことがあります。
  • フラグ判定で && を使うと、ビットの判定ではなく “非0かどうか” の判定になります。
  • ! と ~ はまったく別用途です(! は真偽、~ はビット反転)