トラック6・🔀制御構造

論理演算子と短絡評価

読む目安 約12分・ゴール:&& || ! で複数の条件を組み立てられるようになり、短絡評価の性質を安全のために利用できる

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

遊園地の乗り物に、こんな看板があります。「身長120cm以上、かつ、大人の同伴がある方」。条件が2つ以上あるとき、日本語の「かつ」「または」「〜でない」をCに翻訳するのが論理演算子です。

&& は「かつ」。両方が真のときだけ真になります。

|| は「または」。どちらかが真なら真になります。

! は「〜でない」。真と偽をひっくり返します。

実際に使ってみましょう。

#include <stdio.h>

int main(void) {
    int height = 125;
    bool hasTicket = true;

    if (height >= 120 && hasTicket) {
        printf("ご乗車できます\n");
    } else {
        printf("ごめんなさい、条件を満たしていません\n");
    }
    return 0;
}

hasTicket を && hasTicket == true と書かなくてよいのは、前のトピックのとおりです。

さて、ここで数学の書き方をそのまま持ち込むと事故ります。「x が 0 以上 100 以下」を、こう書きたくなりませんか。

    if (0 <= x <= 100) {        // これは常に真!
        printf("範囲内です\n");
    }

コンパイルは通ります。警告すら出ないことがあります。しかしどんな x でも必ず真になります。Cは左から順に処理するので、まず 0 <= x を判定して true(1) か false(0) という値にします。次に残った式が 1 <= 100 か 0 <= 100 になり、どちらも成り立ってしまうのです。正しくはこう書きます。

#include <stdio.h>

int main(void) {
    int x = 250;

    if (0 <= x && x <= 100) {   // 比較はそれぞれ完全な形で
        printf("範囲内です\n");
    } else {
        printf("範囲外です\n");
    }
    return 0;
}
師範0 <= x <= 100 が通ってしまうのは、比較の結果がまた数値になるからじゃ。Cは律儀に、左から2回比べておる

もうひとつ知っておくべき性質が短絡評価です。A && B は、A が偽だと分かった時点で B を評価しません。結果はもう偽で確定しているからです。同じく A || B は A が真なら B を見ません。これは「たまたまそうなる」のではなく、C言語の仕様で保証された動きです。

この性質は、防波堤として使えます。

#include <stdio.h>

int main(void) {
    int total = 10;
    int count = 0;

    if (count != 0 && total / count > 3) {   // count が 0 なら右は評価されない
        printf("平均は3より大きい\n");
    } else {
        printf("平均は計算できません\n");
    }
    return 0;
}

count が 0 のとき、total / count は0除算でプログラムが異常終了します。しかし左の count != 0 が偽になった瞬間に判定は打ち切られ、割り算は実行されません。もし左右を入れ替えて total / count > 3 && count != 0 と書いたら、その場でクラッシュします。守りの条件を左に置く——これは後々ポインタを扱うときにも命綱になる書き方です。

最後に細かい注意を2つ。&& は || より先に結合するので、混ぜて使うときはかっこを付けて意図をはっきりさせてください。a || b && c は a || (b && c) です。そして ! は結合が強いので、!a == b は (!a) == b になります。迷ったらかっこ、で構いません。

まとめます。&& は「かつ」、|| は「または」、! は「でない」。範囲は 0 <= x && x <= 100。短絡評価のおかげで、左に守りの条件を置けば右は安全に書ける。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

「x が 0 以上 100 以下」を判定したいとき、正しく判定できるのはどれですか。

  1. if (0 <= x <= 100)
  2. if (0 <= x && x <= 100)
  3. if (0 <= x || x <= 100)
  4. if (x >= 0 <= 100)
答えを見る

正解:B(if (0 <= x && x <= 100))

「かつ」は && で表し、比較はそれぞれ完全な形に書きます。0 <= x <= 100 はコンパイルこそ通りますが、まず 0 <= x が true(1) か false(0) という値になり、次に 1 <= 100(または 0 <= 100)を判定するので常に真です。||(または)を使うと、どんな x でもどちらかは成り立つのでこれも常に真。x >= 0 <= 100 も同じ理屈で常に真になります。

確認問題 2

count は 0 です。次のプログラムを実行するとどうなりますか。

#include <stdio.h>

int main(void) {
    int total = 10;
    int count = 0;

    if (count != 0 && total / count > 3) {
        printf("平均は3より大きい\n");
    } else {
        printf("平均は計算できません\n");
    }
    return 0;
}
  1. 「平均は計算できません」と表示される
  2. 「平均は3より大きい」と表示される
  3. 0で割ったためプログラムが異常終了する
  4. コンパイルエラーになる
答えを見る

正解:A(「平均は計算できません」と表示される)

&& は左が偽と分かった時点で右を評価しません(短絡評価)。count は 0 なので count != 0 が偽になり、右側の total / count はそもそも実行されません。だから0除算は起きず、else側の「平均は計算できません」が表示されます。もし条件の左右を入れ替えて total / count > 3 && count != 0 と書くと、先に割り算が実行されて異常終了します。「守りの条件を左に置く」がコツです。

「制御構造」の目次

  1. 流れを変える——制御構造の全体像
  2. if文——もしも、の分かれ道
  3. else if / else の分かれ道設計
  4. 条件式の罠——= と == の悲劇
  5. 論理演算子と短絡評価
  6. switch文とfallthrough
  7. for文——回数が決まった繰り返し
  8. while文——条件が決める繰り返し
  9. do-while文と入力チェック
  10. breakとcontinueの使いどころ
  11. ループのネストと九九表
  12. 無限ループとの付き合い方

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版