C言語のif文・else文の基本と使い方:条件分岐で「もし〜ならA、でなければB」を実装する方法

プログラムで「もし〜ならA、でなければB」という分岐は、読みやすさと安全性を両立させるために基本を正しく押さえることが大切です。

本稿ではC言語のif文・else文の基礎から、真偽の評価、比較・論理演算子、可読性のための作法、実用的な例、典型的な落とし穴、三項演算子との使い分けまでを丁寧に解説します。

基本構文

C言語で条件分岐を行う最も基本的な構文は次のとおりです。

else ifを挟むことで分岐を増やせます。

A・Bの処理が1行でも、波括弧は常につけるのが安全です。

C言語
if (条件式) {
    /* Aの処理 */
} else {
    /* Bの処理 */
}
/* else ifで分岐を追加可能 */

最小例:正・負・ゼロを判定

次のプログラムは入力された整数が正か負かゼロかを判定して表示します。

C言語
#include <stdio.h>

int main(void) {
    int x;
    /* 値の入力を促す */
    printf("整数を入力してください: ");
    if (scanf("%d", &x) != 1) {
        /* 入力失敗を検出 */
        printf("入力エラー\n");
        return 1;
    }

    if (x > 0) {                 /* 正のとき */
        printf("positive\n");
    } else if (x < 0) {          /* 負のとき */
        printf("negative\n");
    } else {                     /* それ以外(ゼロ) */
        printf("zero\n");
    }
    return 0;
}
実行結果
整数を入力してください: 42
positive

条件式の評価(真・偽)

Cでは「真偽値」の根底が数値で表現されます。

0は偽、0以外は真として評価されます。

ポインタではNULLが偽、非NULLが真です。

C99以降はstdbool.hbool/true/falseを使うと意図が明確になります。

0は偽、0以外は真

C言語
#include <stdio.h>
#include <stdbool.h>

int main(void) {
    int a = 0;
    int b = -3;        /* 負でも0でなければ真 */
    bool t = true;     /* 明示的な真偽型 */
    bool f = false;

    if (a) { printf("a is true\n"); }
    if (b) { printf("b is true\n"); }
    if (t) { printf("t is true\n"); }
    if (!f) { printf("f is false\n"); }

    /* 真偽値の数値表現を出力 */
    printf("true=%d, false=%d\n", true, false);
    return 0;
}
実行結果
b is true
t is true
f is false
true=1, false=0

ポインタはNULLが偽、非NULLが真

C言語
#include <stdio.h>

int main(void) {
    int x = 10;
    int *p = &x;
    int *q = NULL;

    if (p) { printf("p is non-NULL\n"); }
    if (!q) { printf("q is NULL\n"); }
    return 0;
}
実行結果
p is non-NULL
q is NULL

比較・論理演算子の要点

条件式では比較演算子と論理演算子を組み合わせます。

代表的なものは次のとおりです。

  • 比較: ==(等しい), !=(等しくない), <, <=, >, >=
  • 論理: &&(AND), ||(OR), !(NOT)
  • &&||は左から短絡評価(ショートサーキット)されます。左側の値だけで結果が確定すると右側は評価されません。

短絡評価の挙動を確認する

次のプログラムは、右側の評価がスキップされる様子を出力で確認します。

C言語
#include <stdio.h>

/* 評価されたことが分かるように表示してから値を返す */
int probe(const char *label, int value) {
    printf("probe(%s) called, returns %d\n", label, value);
    return value;
}

int main(void) {
    puts("ANDの例(左が0=偽)");
    if (probe("L", 0) && probe("R", 1)) {
        puts("AND: true");
    } else {
        puts("AND: false");
    }

    puts("\nORの例(左が1=真)");
    if (probe("L", 1) || probe("R", 0)) {
        puts("OR: true");
    } else {
        puts("OR: false");
    }
    return 0;
}
実行結果
ANDの例(左が0=偽)
probe(L) called, returns 0
AND: false

ORの例(左が1=真)
probe(L) called, returns 1
OR: true

if / else if / else の使い分け

分岐が複数あるときは、条件が「排他的か」「範囲が網羅されるか」を意識し、広い条件を先に書かないように順序を設計します。

C言語
if (x > 10) {
    /* A: 11以上 */
} else if (x > 0) {
    /* B: 1〜10 */
} else {
    /* C: 0以下 */
}

順序が悪い例と良い例

例えばx > 0x > 10を誤って逆にすると、x > 0が先に真になってしまい、後続のx > 10には決して到達しません。

C言語
/* 悪い例:広い条件が先 */
if (x > 0) {
    /* ここで1以上すべてが処理され、x > 10に到達しない */
} else if (x > 10) {
    /* 到達不能 */
}

/* 良い例:狭い条件から先に判定 */
if (x > 10) {
    /* 11以上 */
} else if (x > 0) {
    /* 1〜10 */
} else {
    /* 0以下 */
}

波括弧と可読性のルール

単文でも波括弧{}を常に付けると、後から行を追加した際のバグ(「ぶら下がりelse」や意図しない分岐拡張)を防げます。

また、elseが結びつく相手は字下げではなく構文で決まる点に注意します。

ぶら下がりelseの例

C言語
/* 誤解を招く書き方:インデントと実際の結び付きが異なる */
if (cond1)
    if (cond2)
        puts("A");
else
    puts("B");

/* 実際にはこう解釈される(elseは直前のif(cond2)に結び付く) */
if (cond1) {
    if (cond2) {
        puts("A");
    } else {
        puts("B");
    }
}

/* 推奨:常にブレースを使って意図を明示 */
if (cond1) {
    if (cond2) {
        puts("A");
    }
} else {
    puts("B");
}

実用例(数値判定・入力チェック)

現場で頻出する分岐の具体例を2つ示します。

偶奇で分岐

C言語
#include <stdio.h>

int main(void) {
    int n;
    if (scanf("%d", &n) != 1) {
        puts("input error");
        return 1;
    }
    if (n % 2 == 0) {
        printf("even\n");
    } else {
        printf("odd\n");
    }
    return 0;
}
実行結果
入力: 4
even

入力: 5
odd

入力値の範囲チェック

C言語
#include <stdio.h>

int main(void) {
    int score;
    if (scanf("%d", &score) != 1) {
        puts("input error");
        return 1;
    }
    if (score < 0 || score > 100) {
        printf("invalid\n");
    } else {
        printf("ok\n");
    }
    return 0;
}
実行結果
入力: -5
invalid

入力: 88
ok

よくある落とし穴

日常的に起きやすいバグの種をまとめます。

どれもif-elseの可読性と正しさに直結します。

=(代入)と==(比較)の取り違え

C言語
#include <stdio.h>

int main(void) {
    int x = 123;
    if (x = 0) {             /* 代入してしまっている(xは0に) */
        puts("zero");
    } else {
        puts("non-zero");    /* こちらが必ず実行される(条件は常に偽) */
    }
    return 0;
}
実行結果
non-zero

正しくは比較演算子==を使います。

C言語
#include <stdio.h>

int main(void) {
    int x = 123;
    if (x == 0) {
        puts("zero");
    } else {
        puts("non-zero");
    }
    return 0;
}
実行結果
non-zero

浮動小数点の直接比較は避ける

丸め誤差のため、a == bのような直接比較は意図通りに動かないことがあります。

許容誤差(イプシロン)で比較します。

C言語
#include <stdio.h>
#include <math.h>  /* fabs */

int main(void) {
    double a = 0.1 * 3.0;
    double b = 0.3;
    const double eps = 1e-12;

    printf("a=%.17f, b=%.17f\n", a, b);
    if (fabs(a - b) < eps) {
        puts("ほぼ等しい");
    } else {
        puts("異なる");
    }
    return 0;
}
/* 注: 環境によってはリンク時に -lm が必要です */
実行結果
a=0.30000000000000004, b=0.29999999999999999
ほぼ等しい

& と &&、| と || の混同に注意(ビット演算子と論理演算子は別物)

&|はビット単位の演算子で、短絡評価をしません。

条件式で「両方が真か/どちらかが真か」を判定したいときは&&/||を使います。

短絡評価がないと副作用や例外につながることがあります。

C言語
#include <stdio.h>

int main(void) {
    int x = 0;

    /* 短絡あり:左が偽のため右は評価されず安全 */
    if ((x != 0) && (10 / x > 1)) {
        puts("safe and true");
    } else {
        puts("safe and false");
    }

    /* 短絡なし:両辺が評価されるため0除算が起きる危険 */
    /* if ((x != 0) & (10 / x > 1)) { ... }  ← これは危険 */

    return 0;
}

また、ビットフラグの検査には&を使うのが正しい使い方です。

C言語
/* 例:フラグの検査 */
#define FLAG_A 0x01
#define FLAG_B 0x02

if ((flags & FLAG_A) != 0) {
    /* FLAG_A が立っている */
}

副作用のある条件式に注意

条件式に副作用(変数の変更、関数呼び出しによる状態変更)があると、短絡評価の有無で実行結果が変わります。

C言語
#include <stdio.h>

int call(const char *name) {
    puts(name);
    return 1;
}

int main(void) {
    int i = 0, n = 5, m = 10;

    /* i++ が2回評価される。増分回数が意図通りかを確認 */
    if (i++ < n && i++ < m) {
        /* ここでは i は2増えている可能性がある */
    }
    printf("i=%d\n", i);

    /* 短絡で右が呼ばれないことがある */
    if (0 && call("should not print")) {
        /* 実行されない */
    }
    return 0;
}

副作用は最小化し、条件式では評価と更新を分離して書くと安全です。

三項演算子との比較

短い代入や式として値を返したい場面では、三項演算子が有用です。

C言語
result = cond ? A : B;  /* 短い代入に向く */

使い分けの指針と例

  • 表現(値)を返す用途に適します。複雑な処理や副作用がある場合はif-elseで明確に書きます。
  • ネストした三項演算子は読みにくくなりやすいため、可読性を優先します。
C言語
#include <stdio.h>

int main(void) {
    int x = 7;

    /* 三項演算子:値をそのまま代入したい */
    const char *kind = (x % 2 == 0) ? "even" : "odd";
    printf("%d is %s\n", x, kind);

    /* if-else:出力や複数ステップの処理があるときに向く */
    if (x % 2 == 0) {
        puts("even branch: 追加の処理をここで実行");
    } else {
        puts("odd branch: ロギングや別処理を実行");
    }
    return 0;
}
実行結果
7 is odd
odd branch: ロギングや別処理を実行

比較の観点を表で整理します。

観点三項演算子 ?:if-else
主用途値を返す簡潔な式複数文の処理、分岐の明示
可読性短いとき高い、ネストで低下一般に高い
副作用避けるべき許容しやすいが最小化推奨
デバッグしづらいしやすい(ブレークポイント設置が容易)

まとめ

if-elseは条件分岐の基本であり、正しく使うほどプログラムは読みやすく、安全になります。

条件は「狭いものから広いものへ」「網羅性を意識して」並べ、単文でも必ず波括弧を付けることで将来の変更に強いコードになります。

0/非0の真偽、NULLbool、比較・論理演算子の正しい使い分けと短絡評価の意味を理解し、===&&&の取り違えなど典型的な落とし穴を避けてください。

値を選ぶだけなら三項演算子で簡潔に、複雑な処理ならif-elseで明確にと使い分けるのが実践的です。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!