if文と条件演算子 | Programming Place Plus C言語編 第14章

トップページC言語編

このページの概要 🔗

このページの解説は C99 をベースとしています

以下は目次です。


if文 🔗

if文 (if statement) は、条件式による分岐構造を実現する機能です。条件式を使うので、論理値第13章を参照)が登場します。

分岐構造を実現する方法として、すでに switch文を取り上げています(第11章を参照)。switch文でも分岐構造を表現できるものの、あまり自然には書けない場合も多いです。それは、switch文には以下のように多くの制限があるためです。

  1. 整数しか使えない
  2. 1つの値だけを調べることしかできない
  3. 「一致するかどうか」という判定しかできない
  4. 比較相手は定数でなければならない

1番の制限のため、文字列を比較することができません。2番の制限のため、2つ以上の変数の値に応じた分岐を書けません(「a と b の値が同じだったら」など)。3番の制限のため、大小関係の比較ができませんし、4番の制限のため、入力された値と比べるといったことができません。

こういった多くの制限がなく、自由度の高い分岐が実現できる方法が if文です。

if文の文法は次のようになっています。

if (条件式)
    // 条件式の結果が 0以外のときに実行する文

if文の「条件式」は結果的に数値にさえなれば何でも書けます。if (value == 10)if (value < value2) のように、等価演算子第13章を参照)や関係演算子第13章を参照)を使った式を置いてもいいですし、単に if (value) のように変数名だけが置かれてもいいです。

【C++プログラマー】C++ では「条件式」は bool型の値になるものを求められますが、実質的には違いはありません(数値は bool型に変換できるので)。

if文が実行されるときには、まず「条件式」が評価されます。その値が 0以外になったとき(つまり、論理値でいえば「真」のとき)にだけ「条件式の結果が 0以外のときに実行する文」が実行されます。0 になったときには、if文全体が飛ばされます。

for文のときと同様、構文上は「条件式の結果が 0以外のときに実行する文」に書ける文は1つだけです。複数の文を書きたいときには、{} によるブロック文を導入します。文が1つだけであっても、ブロック文を導入することに問題はありません。つねにブロック文にすることを勧めます。


if文を使ったプログラムの例は次のようになります。入力されたスコアが 100 のときにだけ、追加の文章が出力されます。

#include <stdio.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the your score.");
    int score = get_input_integer();

    printf("Your score: %d\n", score);
    if (score == 100) {
        puts("Great!!");
    }
}

実行結果:

Please enter the your score.
100  <-- 入力したスコア
Your score: 100
Great!!
Please enter the your score.
50  <-- 入力したスコア
Your score: 50

else(条件を満たさないとき) 🔗

さきほどの if文では、条件式の結果が 0 になったときに実行する文がありません。これは switch文なら、defaultラベルを使って実現できたことです。

if文には、条件式が 0 にならなかったときにだけ実行する文を書く機能があります。文法は次のようになります。

if (条件式)
    // 条件式の結果が 0以外 のときに実行する文
else
    // 条件式の結果が 0 のときに実行する文

このように、else というキーワードを用います。プログラムを実行し、if文のところにやってくると、「条件式」が評価され、その値が 0以外になったとき(真のとき)にだけ「条件式の結果が 0以外 のときに実行する文」が、0 になったとき(偽のとき)にだけ「条件式の結果が 0 のときに実行する文」が実行されます。つまり、どちらか一方だけしか実行されることはありません。

もちろん、else のところもブロック文を使って、複数の文を書けます。

if (value == 0) {
    // 条件式の結果が 0以外 のときに実行する文
}
else {
    // 条件式の結果が 0 のときに実行する文
}

なお、0 になったときに実行する文だけが必要で、0以外になったときにすることがない場合、「条件式の結果が 0以外 のときに実行する文」を空にしておくことができます。このとき {} でブロック文にしておかないといけません。

// OK
if (value == 0) {
}
else {
    printf("%d\n", value);
}
// エラー。else以下を「条件式の結果が 0以外 のときに実行する文」だと判断してコンパイルしようとする
if (value == 0)
else {
    printf("%d\n", value);
}

そもそも、条件式を逆転させて else をなくしたほうが分かりやすい可能性があるので、どちらが理解しやすいか検討するようにしましょう。

// さきほどと同じ意味
if (value != 0) {
    printf("%d\n", value);
}

== の逆は !=!= の逆は == で、これは分かりやすいですが、大小関係の逆は注意しましょう。< の逆は >= ですし、> の逆は <= です。


else を使った if文のプログラムは次のようになります。入力されたスコアに応じて、合格・不合格を判断します。

#include <stdio.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the your score.");
    int score = get_input_integer();

    printf("Your score: %d\n", score);
    if (score >= 60) {
        puts("Pass.");
    }
    else {
        puts("Failure.");
    }
}

実行結果:

Please enter the your score.
70  <-- 入力したスコア
Your score: 70
Pass.
Please enter the your score.
40  <-- 入力したスコア
Your score: 40
Failure.

if と else の連続(複数の条件判定) 🔗

else を使って、条件式を満たすときと満たさないときに分岐できるようになりました。しかし、switch文なら3つ以上の選択肢が書けたのですから、まだ if文のほうが劣っている感じがします。

if文でも、3つ以上の処理のいずれかを実行させる分岐構造が作れます。そのためには、else のところに if文を書いて、if と else を繰り返していきます。たとえば、次のようになります。

if (条件式A)
    // 条件式Aの結果が 0以外のときに実行する文
else if (条件式B)
    // 条件式Aの結果が 0 で、かつ条件式Bの結果が 0以外のときに実行する文
else if (条件式C)
    // 条件式Aと条件式Bの結果がいずれも 0 で、かつ条件式Cの結果が 0以外のときに実行する文
else
    // 条件式A、条件式B、条件式Cの結果がいずれも 0 のときに実行する文

else if の連鎖はどこまでも続けられます(コンパイラがどこかで限界を設けているかもしれませんが、実用上、気にすることはありません)。最後の1つだけは単体の else にしてもよく、「結局どれにも一致しなかったとき」に実行する文を記述できます(switch文でいうところの default に当たるもの)。最後の1つも else if でも構いません。その場合は、「結局どれにも一致しなかったとき」にすることは何もないということになります。

どんなに複雑にみえても、どんなに長大になっていたとしても、上から順に1つずつ条件式が調べられていくイメージを持てば問題ありません。

まず先頭の条件式Aを評価します。これが 0以外なら「条件式Aの結果が 0以外のときに実行する文」を実行したあと、一気に最後まで移動して終了します。0 だったなら、次の else if へ進みます。

else if (条件式B) では条件式Bを評価して、0以外なら「条件式Aの結果が 0 で、かつ条件式Bの結果が 0 なら、次の else if へ進みます。以下は同様です。

else if がたくさん並んでいても、条件を満たして実行される文はどれか1つだけです(あるいは、どの条件も満たさなかったうえに単体の else もないため、どれも実行されないか)。

【上級】そのため、早く登場する if文で 0以外になった方が、プログラムの実行速度は速いことになります。


else if の連続を用いたプログラムは、次のようになります。入力されたスコアに応じて、3つの文字列のいずれかが出力されます。

#include <stdio.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the your score.");
    int score = get_input_integer();

    printf("Your score: %d\n", score);
    if (score == 100) {
        puts("Great!!");
    }
    else if (score >= 60) {
        puts("Pass!!");
    }
    else {
        puts("Failure!!");
    }
}

実行結果:

Please enter the your score.
100  <-- 入力したスコア
Your score: 100
Great!!
Please enter the your score.
70  <-- 入力したスコア
Your score: 70
Pass.
Please enter the your score.
40  <-- 入力したスコア
Your score: 40
Failure.

なお、ifelse if の条件式はそれぞれ、まったく関係性がないことでも構いません。たとえば、if (value > 0) の次に登場する条件式が str == "Hello" であっても構いません。これは、調べる対象が1つに固定されている switch文にはない自由さです。

switch文のように、3つ以上の選択肢が if文でも書けるからといって(そして、他の面でも制約が少ないからといって)、switch文に使い道がないということではありません。「ある値が、いくつかの定数のどれに一致するか」というかたちを取る場面では、switch文で書いた方が意図が明確です。

条件演算子 🔗

条件に応じて処理を分岐させるというよりは、「条件に応じた値が欲しい」という場面があります。そういう場合でも if文を使って書けます。

#include <stdio.h>
#include <string.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the your score.");
    int score = get_input_integer();

    char judge[40] = "";
    if (score >= 70) {
        strcpy(judge, "Pass.");
    }
    else {
        strcpy(judge, "Failure.");
    }

    puts(judge);
}

実行結果:

Please enter the your score.
70  <-- 入力したスコア
Pass.

入力されたスコアに応じて異なる文字列を、変数 judge に格納しています。これでも目的は果たせますが、ややコード量が長く、行数が多くなりがちです。また、strcpy(judge, ****); という、同じ目的の同じかたちのコードが重複していることもやや気になります。そうそうあるとは思えませんが、間違って片方だけ別の変数にコピーしてしまうようなミスが起こるかもしれません。

そこで、もう1つ別の方法として、条件演算子 (conditional operator) があります。条件演算子は次の構文で使用します。

条件式 ? 0以外の場合に実行する式 : 0 の場合に実行する式

条件演算子は、オペランドが3つある珍しい(C言語では唯一の)演算子です。3つのオペランドを区切るように ?: という2つの記号を使います。

条件演算子を使った式が実行されるとき、まず「条件式」が評価さます。0以外だった場合は「0以外の場合に実行する式」が、0 だった場合は「0 の場合に実行する式」が評価されます。いずれかの式を評価した結果が、全体としての評価結果になります。どちらの結果が評価結果になるかわからないので、「0以外の場合に実行する式」と「0 の場合に実行する式」を評価した結果の型は同じにならなければなりません。

さきほどのプログラムを条件演算子で書き換えると、次のようになります。

#include <stdio.h>
#include <string.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the your score.");
    int score = get_input_integer();

    char judge[40] = "";
    strcpy(judge, score >= 70 ? "Pass." : "Failure.");

    puts(judge);
}

実行結果:

Please enter the your score.
70  <-- 入力したスコア
Pass.

if文と違って、式として記述できるため(値になるので)、strcpy関数の実引数のところに埋め込むことができます。

【上級】char judge[40] = score >= 70 ? "Pass." : "Failure."; のようにしたくなるかもしれませんが、これはコンパイルエラーになります。文字列リテラルの型は char型の配列ですが、条件演算子の第2第3オペランドに配列は置けず、ポインタに変換されることによって許されている状態です。ポインタから配列への逆の変換は起こらないので、配列の初期化子として不正ということになります。char* judge = score >= 70 ? "Pass." : "Failure."; ならば許されます。

もちろん変数 judge が不要なら、puts関数のほうにまとめられます。

puts(score >= 70 ? "Pass." : "Failure.");


練習問題 🔗

問題① 次のプログラムを実行したとき、B が出力されるのは、変数 value の値が以下のいずれのときですか? すべて選んでください。

#include <stdio.h>

int main(void)
{
    int value;

    if (value <= 10) {
        puts("A");
    }
    else {
        puts("B");
    }
}

問題② switch文で書かれた次の分岐構造を、if文で書き換えてください。

#include <stdio.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the integer.");
    int value = get_input_integer();

    switch (value) {
    case 0:
        puts("A");
        break;
    case 1:
        puts("B");
        break;
    case 2:
        puts("C");
        break;
    default:
        puts("X");
        break;
    }
}

問題③ 2つの整数の入力を受け取り、大きい方の値を出力するプログラムを作成してください。if文を使う場合と、条件演算子を使う場合とで書いてみてください。

問題④ 入力されたスコアに応じて、以下のように * を出力したいとします。

そこで、次のようなプログラムを書きましたが、これは間違っています。その理由を答え、正しいプログラムを書いてください。

#include <stdio.h>

// 整数の入力を受け取る
// 戻り値: 標準入力から整数の入力を受け取り、その値を返す。
int get_input_integer(void)
{
    char s[40];
    fgets(s, sizeof(s), stdin);
    int value;
    sscanf(s, "%d", &value);
    return value;
}

int main(void)
{
    puts("Please enter the score.");
    int score = get_input_integer();

    if (score < 40) {
        printf("\n");
    }
    else if (score >= 40) {
        printf("*\n");
    }
    else if (score >= 70) {
        printf("**\n");
    }
    else if (score == 100) {
        printf("***\n");
    }
}

問題⑤ 1 から 50 までの整数の中で、4 の倍数のものだけを出力するプログラムを作成してください。ただし、for文と if文を使うようにしてください。

実行結果はこうなります。

4
8
12
16
20
24
28
32
36
40
44
48


解答ページはこちら

参考リンク 🔗


更新履歴 🔗



前の章へ (第13章 論理値)

次の章へ (第15章 論理演算)

C言語編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る