C言語を習得する上で、プログラムの流れを制御する「条件分岐」や「繰り返し処理」の理解は欠かせません。

その中でも、複数の条件を組み合わせたり、条件を反転させたりする際に必要不可欠となるのが論理演算子です。

論理演算子を正しく使いこなすことで、複雑なロジックを簡潔かつ正確に記述できるようになります。

本記事では、AND、OR、NOTという3つの主要な論理演算子について、その基礎から応用、さらには注意すべき優先順位や短絡評価の仕組みまで、初心者の方にも分かりやすく徹底的に解説します。

C言語における真偽値の定義

論理演算子を深く理解する前に、まずはC言語における「真(true)」と「偽(false)」の扱いについて整理しておきましょう。

他のプログラミング言語では専用の型が用意されていることも多いですが、伝統的なC言語においては数値によって真偽を判定します。

C言語では、以下のルールに従って条件式の結果が評価されます。

判定結果解説
0偽(false)整数値の0、またはNULLポインタなどが該当します。
0以外真(true)1はもちろん、負の数や小数であっても0でなければすべて真とみなされます。

C99規格以降では、stdbool.hをインクルードすることでbool型やtruefalseというキーワードを使用できるようになりましたが、内部的には依然として0が偽、1が真として扱われています。

この「0以外はすべて真である」という性質は、論理演算子を利用する際に非常に重要なポイントとなります。

論理積(AND)演算子:&&

論理積(AND)演算子は、記号 && を使用します。

この演算子は、2つの条件式の両方が「真」である場合に、全体の結果として「真」を返します。

AND演算子の基本動作

例えば、「年齢が20歳以上」かつ「免許を持っている」という2つの条件が揃ったときのみ処理を行いたい場合に利用します。

どちらか一方でも「偽」であれば、全体の結果は「偽」となります。

以下に論理積の真理値表を示します。

式1式2式1 && 式2

AND演算子のサンプルコード

実際にAND演算子を使用したプログラムを見てみましょう。

このプログラムは、入力された数値がある範囲内に収まっているかどうかを判定します。

C言語
#include <stdio.h>

int main(void) {
    int score = 85;

    // scoreが80以上、かつ100以下であるかを確認
    if (score >= 80 && score <= 100) {
        printf("結果: 優秀です。\n");
    } else {
        printf("結果: 範囲外、または基準に達していません。\n");
    }

    return 0;
}
実行結果
結果: 優秀です。

この例では、score >= 80 が真であり、かつ score <= 100 も真であるため、全体の条件が真となり、メッセージが表示されます。

論理和(OR)演算子:||

論理和(OR)演算子は、記号 || を使用します。

この演算子は、2つの条件式のうち少なくとも一方が「真」であれば、全体の結果として「真」を返します。

OR演算子の基本動作

「土曜日である」または「日曜日である」という条件のように、いずれか一方を満たせばよい場合に利用します。

両方が「偽」である場合のみ、全体の結果が「偽」となります。

以下に論理和の真理値表を示します。

式1式2式1 || 式2

OR演算子のサンプルコード

OR演算子を使用して、特定の曜日(数値で代用)を判定するプログラムです。

C言語
#include <stdio.h>

int main(void) {
    int day = 6; // 6を土曜日、0を日曜日とする

    // dayが6(土曜)または0(日曜)であるかを確認
    if (day == 6 || day == 0) {
        printf("今日は休日です。\n");
    } else {
        printf("今日は平日です。\n");
    }

    return 0;
}
実行結果
今日は休日です。

この場合、day == 6 が真であるため、day == 0 の結果に関わらず、全体として真と判定されます。

論理否定(NOT)演算子:!

論理否定(NOT)演算子は、記号 ! を使用します。

この演算子は、その後に続く条件式の真偽を反転させます。

つまり、式が真であれば「偽」に、偽であれば「真」になります。

NOT演算子の基本動作

「フラグが立っていない場合」や「値が0でない場合」など、条件の否定を表現したいときに非常に便利です。

!式

NOT演算子のサンプルコード

NOT演算子を使って、ログイン状態を判定する例を見てみましょう。

C言語
#include <stdio.h>

int main(void) {
    int is_logged_in = 0; // 0は偽(ログインしていない)

    // 「ログインしていない」状態を判定
    if (!is_logged_in) {
        printf("ログインが必要です。ログイン画面へ移動します。\n");
    } else {
        printf("ログイン済みです。マイページを表示します。\n");
    }

    return 0;
}
実行結果
ログインが必要です。ログイン画面へ移動します。

is_logged_in は 0(偽)ですが、! 演算子によって真に反転されます。

その結果、if 文の中身が実行されます。

短絡評価(ショートサーキット評価)の重要性

C言語の論理演算子には、短絡評価(ショートサーキット評価)という重要な特性があります。

これは、全体の結果が確定した時点で、それ以降の式の評価を打ち切る仕組みのことです。

ANDにおける短絡評価

式1 && 式2 において、もし 式1 が偽であれば、式2 が何であっても全体は必ず偽になります。

そのため、C言語のコンパイラは式2を評価(計算)しません

ORにおける短絡評価

式1 || 式2 において、もし 式1 が真であれば、全体は必ず真になります。

そのため、この場合も式2は評価されません

短絡評価のメリットと注意点

この仕組みは、エラーの回避に役立ちます。

例えば、ポインタが NULL でないことを確認してからその中身を参照する場合などに多用されます。

C言語
#include <stdio.h>

int main(void) {
    int *ptr = NULL;

    // ptrがNULLでないことを先に確認する
    // 短絡評価により、ptrがNULLなら後半の *ptr は実行されない
    if (ptr != NULL && *ptr == 10) {
        printf("値は10です。\n");
    } else {
        printf("ポインタがNULL、または値が10ではありません。\n");
    }

    return 0;
}

もし短絡評価がなければ、ptrNULL であっても *ptr を評価しようとしてしまい、プログラムがクラッシュする原因となります。

一方で、式2 の中に変数への代入や関数の呼び出しといった副作用を含めている場合、それが実行されないことがあるため注意が必要です。

論理演算子の優先順位と結合規則

複数の論理演算子や比較演算子を一つの式に記述する場合、どの演算から順番に処理されるかを知っておく必要があります。

演算子の優先順位表

C言語の主要な演算子の優先順位(高い順)は以下の通りです。

順位演算子種類
1!論理否定(単項演算子)
2*, /, %算術演算
3+, -算術演算
4<, <=, >, >=比較(大小)
5==, !=比較(等価)
6&&論理積(AND)
7||論理和(OR)

この表から分かる通り、&&|| よりも優先順位が高いです。

例えば、A || B && C という式は、A || (B && C) と同じ意味になります。

カッコによる明示的な順序指定

優先順位を暗記してコードを書くのは、バグの原因になりやすく、また可読性も低下させます。

複雑な条件式を書く際は、カッコ ( ) を使用して演算の順序を明示することが強く推奨されます。

C言語
// どちらが実行されるか分かりにくい例
if (a > 0 || b > 0 && c > 0) 

// 意図が明確な例
if (a > 0 || (b > 0 && c > 0))

このようにカッコを使うことで、自分以外の開発者がコードを読んだ際の誤解を防ぐことができます。

ビット演算子との違いに注意

C言語には、論理演算子とよく似た記号を持つ「ビット演算子」が存在します。

初心者が非常に間違えやすいポイントですので、しっかりと区別しましょう。

  • && (論理積) vs & (ビット単位の論理積)
  • || (論理和) vs | (ビット単位の論理和)

論理演算子は、式全体の真偽(0か1か)を判定するためのものです。

一方、ビット演算子は数値の各ビット(2進数レベル)に対して演算を行います。

例えば、1 && 2 は、1も2も「真(0以外)」なので結果は 1(真)になります。

しかし、1 & 2 は、2進数の 0110 の論理積をとるため、結果は 0 になります。

条件分岐で誤ってビット演算子を使ってしまうと、予期しない動作を引き起こす可能性があるため、記号の数には十分に注意してください。

実践的な使用例:複雑な条件の構築

論理演算子を組み合わせることで、実務でよく使われる高度な条件判定が可能になります。

範囲の判定(ANDの活用)

ある値が特定の範囲に含まれている、あるいは含まれていないことを判定する例です。

C言語
#include <stdio.h>

int main(void) {
    int temperature = 25;

    // 15度以上かつ30度以下のとき「快適」
    if (temperature >= 15 && temperature <= 30) {
        printf("過ごしやすい気温です。\n");
    }

    // 10度未満、または35度より高いとき「極端」
    if (temperature < 10 || temperature > 35) {
        printf("厳しい気温です。注意してください。\n");
    }

    return 0;
}

多重フラグの管理(NOTとANDの併用)

システムが動作可能かどうかを、複数のステータスから判断する例です。

C言語
#include <stdio.h>

int main(void) {
    int is_error = 0;      // エラーが発生していない(0)
    int is_ready = 1;      // 準備ができている(1)
    int maintenance = 0;   // メンテナンス中ではない(0)

    // 「エラーがなく」かつ「準備ができており」かつ「メンテナンス中でない」
    if (!is_error && is_ready && !maintenance) {
        printf("システムは正常に稼働しています。\n");
    } else {
        printf("システムを稼働できません。\n");
    }

    return 0;
}

このように、! を使って「〜でない」という条件を直感的に記述できます。

まとめ

C言語の論理演算子(AND, OR, NOT)は、プログラムにインテリジェンスな判断を行わせるための強力なツールです。

  • && (AND) は、全ての条件が満たされたときに真となる。
  • || (OR) は、いずれか一つの条件が満たされたときに真となる。
  • ! (NOT) は、真偽を逆転させる。
  • 短絡評価の仕組みにより、不要な評価をスキップして効率と安全性を高めている。
  • 優先順位に注意し、必要に応じてカッコ ( ) を活用する。

これらの基本ルールを正しく理解し、ビット演算子との混同を避けることで、より堅牢で読みやすいソースコードを記述できるようになります。

論理演算はプログラミングの基礎中の基礎ですが、複雑な条件をスマートに記述できるかどうかが、プログラマとしての腕の見せ所でもあります。

ぜひ日々のコーディングで意識して活用してみてください。