C言語学習ポータル(無料版) 学習コンテンツ 制御構造 条件式の罠——= と == の悲劇

🔀 制御構造

条件式の罠——= と == の悲劇

無料公開版に収録・読む目安 10分

🎯 代入 = と比較 == の違いを説明でき、条件式で取り違えたときに何が起きるかを見抜ける

算数で習った「=」は「等しい」という意味でした。C言語の = は「入れる」です。この1文字の食い違いが、C言語で最も有名な事故を生みます。

仕組みから見ていきましょう。

a = 5 は代入です。そして重要なのは、代入した結果、式全体が「代入した値」を持つという点。つまり a = 5 という式そのものの値は 5 です。

a == 5 は比較です。式の値は真か偽になります。

ということは、if (a = 5) はこう読まれます。「a に 5 を入れて、その結果の 5 を条件として見る」。5 は 0 以外なので——常に真です。しかもコンパイルは通ります。

#include <stdio.h>

int main(void) {
    int stock = 0;

    if (stock = 5) {          // 本当は stock == 5 と書きたかった
        printf("在庫は5個です\n");
    } else {
        printf("在庫は5個ではありません\n");
    }
    printf("stock = %d\n", stock);
    return 0;
}

在庫は 0 のはずなのに、「在庫は5個です」「stock = 5」と表示されます。判定するだけのつもりが、変数の中身まで書き換えてしまったのです。あとの行で stock を使う処理があれば、そこも全部おかしくなります。バグの発生地点と症状が出る場所が離れるので、原因にたどり着くのに時間がかかる——これが「悲劇」と呼ばれるゆえんです。

師範= は「入れる」、== は「同じか?」。この1文字の差で、判定が代入にすり替わる。世界中のC書きが一度は通る道じゃ

対策は3つあります。

1. コンパイラの警告を有効にする。 gcc や clang なら -Wall を付けるだけで「代入を条件として使っていますよ」と教えてくれます。人間の注意力より、機械のチェックのほうが確実です。

2. 定数を左に置く(ヨーダ記法)。 if (5 == stock) と書いておけば、うっかり = を1つ落として if (5 = stock) としてもコンパイルエラーになります。定数には代入できないからです。読みにくいという意見もありますが、確実に事故を1種類つぶせます。

#include <stdio.h>

int main(void) {
    int stock = 0;

    if (5 == stock) {         // 書き間違えたら即コンパイルエラー
        printf("在庫は5個です\n");
    } else {
        printf("在庫は5個ではありません\n");
    }
    return 0;
}

3. 読み上げる。 if (stock == 5) を「もし stock が 5 と等しいなら」と口に出す。= が1つだと「もし stock に 5 を入れるなら」となり、日本語として明らかにおかしいので気づけます。

最後に、bool型との比較について。真偽値を == true と比べたくなりますが、その必要はありません。

    bool isOpen = true;

    if (isOpen == true) { /* … */ }   // 動くが回りくどい
    if (isOpen) { /* … */ }           // こう書けばよい

条件式はもともと真偽を見る場所なので、bool 変数はそのまま置けます。比較を書かなければ、= と == を間違えようもありません。

まとめます。= は代入、== は比較。代入式の値は代入した値そのもの。if (x = 5) は常に真になり、おまけに x を壊す。警告を有効にして、bool はそのまま書く。

要点まとめ

アプリで演習問題まで解く(無料・登録不要)

この項目に出てくる用語

if文いふぶん

条件が真のときだけ、特定の処理を実行させる制御構造のこと。プログラムの中に「もしも」という分かれ道を作る、いちばん基本の道具。条件が偽のときは何もせず、そのまま次の処理へ進んでいく。

条件式じょうけんしき

真(true)か偽(false)のどちらかに評価される式のこと。if文やwhile文が「どちらへ進むか」を判断するための材料になる。true/falseで表される真偽値の型(bool)がこの評価結果にあたる。

演算子えんざんし

+や-、==など、値どうしを計算したり比較したりするための記号のこと。演算子ごとに計算される優先順位があらかじめ決まっている。+ - * / のような算術演算子のほか、比較や論理の演算子も含まれる。

bool型ぶーるがた

true(真)かfalse(偽)のどちらかしか入らない型のこと。C23では標準の書き方としてbool・true・falseがそのまま使えるようになった。従来は0と1で真偽を表していたが、C23の書き方の方が意図が伝わりやすい。

変数へんすう

値を入れておくための名前付きの箱のこと。名前をつけることで中身を自由に出し入れできる、プログラムの記憶場所にあたる。変数名は自由に付けられるが、意味の分かる名前にすると読みやすいコードになる。

同じトラックのほかの項目

流れを変える——制御構造の全体像プログラムの流れが「順次・分岐・繰り返し」の3つでできていることを理解し、場面に応じてどれを使うか見当をつけられ… if文——もしも、の分かれ道if文の書き方と条件式の考え方を理解し、条件によって処理を分けられるようになる else if / else の分かれ道設計else / else if で複数の分かれ道を作れるようになり、範囲判定の順序を正しく設計できる 論理演算子と短絡評価&& || ! で複数の条件を組み立てられるようになり、短絡評価の性質を安全のために利用できる switch文とfallthroughswitch文で値による多分岐を書けるようになり、break忘れによるフォールスルーを理解する for文——回数が決まった繰り返しfor文の3つの部品と実行順序を理解し、回数の決まった繰り返しを正しく書ける while文——条件が決める繰り返しwhile文で終わりの読めない繰り返しを書けるようになり、for との使い分けを判断できる do-while文と入力チェックdo-while の「まず1回は実行する」性質を理解し、入力のやり直しループを書ける breakとcontinueの使いどころbreak でループを打ち切り、continue で残りをスキップできるようになり、それぞれの副作用を避けられる ループのネストと九九表二重ループの動きを説明でき、九九表のような表形式の出力を自分で組み立てられる 無限ループとの付き合い方無限ループの原因を3つの型で診断でき、意図的な無限ループを出口つきで安全に書ける
‹ else if / else の分かれ道設計 論理演算子と短絡評価 ›

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

完全版(BOOTH)を見る

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

Web版 / BOOTH