先頭へ戻る

if文と条件演算子 | Programming Place Plus 新C++編

Programming Place Plus トップページ -- 新C++編

先頭へ戻る

このページの概要

このページでは、if文と条件演算子を取り上げます。この2つの機能は、何らかの条件によって処理を分岐させるものです。処理を分岐させる機能としては、すでに switch文を取り上げていますが、if文や条件演算子は、論理値を用いた、より柔軟な条件での分岐を実現できます。

以下は目次です。要点だけをさっと確認したい方は、「まとめ」をご覧ください。



if文

if文 (if statement) は、条件式による分岐構造(用語集)を実現する機能です。条件式を使うので、論理値(用語集)(「論理値」のページを参照)が登場します。

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

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

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

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

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

if (条件式)
    // 条件式が true のときに実行する文

if文の「条件式」には、bool型の値になるものならば何でも書けます。if (value == 10)if (value < value2) のように、等価演算子(「論理値」のページを参照)や関係演算子(「論理値」のページを参照)を使った式(用語集)を置いてもいいですし、bool型の値が得られるのならば、単に if (value) のように変数名だけが置かれてもいいです。

int型や char型の値は bool型に変換できます(「論理値」のページを参照)。std::string型からは変換できないので、(s が std::string型として)if (s) とは書けません。

if文が実行されるときには、まず「条件式」が評価(用語集)され、bool型の値を得ます。その値が true になったときにだけ「条件式が true のときに実行する文」が実行されます。false になったときには、if文全体が飛ばされます。

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


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

#include <iostream>

int main()
{
    std::cout << "Please enter the your score.\n";
    int score {};
    std::cin >> score;

    std::cout << "Your score: " << score << "\n";
    if (score == 100) {
        std::cout << "Great!!\n";
    }
}

実行結果:

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

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

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

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

if (条件式)
    // 条件式が true のときに実行する文
else
    // 条件式が false のときに実行する文

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

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

if (value == 0) {
    // 条件式が true のときに実行する文
}
else {
    // 条件式が false のときに実行する文
}

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

// OK
if (value == 0) {
}
else {
    std::cout << value << "\n";
}

// エラー。else以下を「条件式が true のときに実行する文」だと判断してコンパイルしようとする
if (value == 0)
else {
    std::cout << value << "\n";
}

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

// さきほどと同じ意味
if (value != 0) {
    std::cout << value << "\n";
}

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


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

#include <iostream>

int main()
{
    std::cout << "Please enter the your score.\n";
    int score {};
    std::cin >> score;

    std::cout << "Your score: " << score << "\n";
    if (score >= 60) {
        std::cout << "pass.\n";
    }
    else {
        std::cout << "failure.\n";
    }
}

実行結果:

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が true のときに実行する文
else if (条件式B)
    // 条件式Aが false で、かつ条件式Bが true のときに実行する文
else if (条件式C)
    // 条件式Aと条件式Bが false で、かつ条件式Cが true のときに実行する文
else
    // 条件式A、条件式B、条件式Cがいずれも false のときに実行する文

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

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

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

else if (条件式B) では条件式Bを評価して、true なら「条件式Aが false で、かつ条件式Bが true のときに実行する文」が実行されて終了。false なら、次の else if へ進みます。以下は同様です。

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

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


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

#include <iostream>

int main()
{
    std::cout << "Please enter the your score.\n";
    int score {};
    std::cin >> score;

    std::cout << "Your score: " << score << "\n";
    if (score == 100) {
        std::cout << "Great!!\n";
    }
    else if (score >= 60) {
        std::cout << "pass.\n";
    }
    else {
        std::cout << "failure.\n";
    }
}

実行結果:

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 <iostream>
#include <string>

int main()
{
    std::cout << "Please enter the your score.\n";
    int score {};
    std::cin >> score;

    std::string judge {};
    if (score >= 70) {
        judge = "pass";
    }
    else {
        judge = "failure";
    }

    std::cout << judge << "\n";
}

実行結果:

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

入力されたスコアに応じて異なる文字列を、変数 judge に代入しています。これでも目的は果たせますが、一旦、空の状態で宣言してから、値を代入していることが少し気になるところです。こういう作りになっていると、本当にどこかで正しい文字列が代入されるのか(されているか)を気にしなければなりません。できることなら、変数の値は宣言と同時に決まり、そのまま変化しないことが、分かりやすさの観点からは望ましいです(「代入」のページを参照)。

また、judge = ****; という、同じ目的の同じかたちのコードが重複していることもやや気になります。そうそうあるとは思えませんが、間違って片方だけ別の変数に代入してしまうようなミスが起こるかもしれません。

こういった気になるポイントを解消するために、条件演算子(用語集) (conditional operator) が利用できます。条件演算子は次の構文で使用します。

条件式 ? true の場合に実行する式 : false の場合に実行する式

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

条件演算子を使った式が実行されるとき、まず「条件式」が評価され、true か false を得ます。true だった場合は「true の場合に実行する式」が、false だった場合は「false の場合に実行する式」が評価されます。いずれかの式を評価した結果が、全体としての評価結果になります。

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

#include <iostream>
#include <string>

int main()
{
    std::cout << "Please enter the your score.\n";
    int score {};
    std::cin >> score;

    std::string judge {score >= 70 ? "pass" : "failure"};

    std::cout << judge << "\n";
}

実行結果:

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

これなら、変数 judge は確実に初期化されていると自信をもっていえます。何かを誤る可能性が減っています。

この場合は、変数 judge ももはや不要かもしれません。もしそうであれば、次のように書いてしまえます。構文の解釈の都合上、( ) を補う必要があります。

std::cout << (score >= 70 ? "pass" : "failure") << "\n";

条件によって変数の初期値を切り替えたり、std::cout の文の中で切り替えたりできるのは、条件演算子が結果の値を返してくれているからです。言い換えると、score >= 70 ? "pass" : "failure" が「式」だからです(式は値を返す)。if文は名前のとおり「文」ですから、こういうことはできないのです(文は値を返さない)。

まとめ


新C++編の【本編】の各ページには、末尾に練習問題があります。ページ内で学んだ知識を確認する簡単な問題から、これまでに学んだ知識を組み合わせなければならない問題、あるいは更なる自力での調査や模索が必要になるような高難易度な問題をいくつか掲載しています。


参考リンク


練習問題

問題の難易度について。

★は、すべての方が取り組める入門レベルの問題です。
★★は、自力でプログラミングができるようなるために、入門者の方であっても取り組んでほしい問題です。
★★★は、本格的にプログラマーを目指す人のための問題です。

問題1 (確認★)

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

#include <iostream>

int main()
{
    int value {};

    if (value <= 10) {
        std::cout << "A\n";
    }
    else {
        std::cout << "B\n";
    }
}

解答・解説

問題2 (基本★)

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

#include <iostream>

int main()
{
    std::cout << "Please enter the integer.\n";
    int value {};
    std::cin >> value;

    switch (value) {
    case 0:
        std::cout << "A\n";
        break;
    case 1:
        std::cout << "B\n";
        break;
    case 2:
        std::cout << "C\n";
        break;
    default:
        std::cout << "X\n";
        break;
    }
}

解答・解説

問題3 (基本★)

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

解答・解説

問題4 (基本★★)

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

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

#include <iostream>

int main()
{
    std::cout << "Please enter the score.\n";
    int score {};
    std::cin >> score;

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

解答・解説

問題5 (応用★★)

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

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

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

解答・解説

問題6 (応用★★)

for文」のページで取り上げた電卓プログラム(ソースコードは以下に再掲)を改造して、ゼロ除算を回避するようにしてください。たとえば、"zero divide." のような文字列を出力するといった対応をしてください。

#include <iostream>
#include <string>

int main()
{
    constexpr auto ADDITION = '+';
    constexpr auto SUBTRACTION = '-';
    constexpr auto MULTIPLICATION = '*';
    constexpr auto DIVISION = '/';

    for (int i = 0; i < 5; ++i) {
        std::cout << "Please enter the formula.\n";
        int value1 {};
        std::string op {};
        int value2 {};
        std::cin >> value1 >> op >> value2;

        switch (op[0]) {
        case ADDITION:
            std::cout << value1 + value2 << "\n";
            break;
        case SUBTRACTION:
            std::cout << value1 - value2 << "\n";
            break;
        case MULTIPLICATION:
            std::cout << value1 * value2 << "\n";
            break;
        case DIVISION:
            std::cout << value1 / value2 << "\n";
            break;
        default:
            std::cout << "The formula is incorrect.\n";
            break;
        }
    }
}

解答・解説


解答・解説ページの先頭



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー