C言語編 第11章 処理の流れを分岐させる

先頭へ戻る

この章の概要

この章の概要です。


分岐構造

これまでのプログラムは、main関数から始まった処理は、順番に下へ下へ(あるいは右へ)と流れていき、main関数の終了とともに終わるという一直線なものばかりでした。このように、次の命令、次の命令へと進んでいく処理構造は、順次構造と呼ばれることがあります。

順次構造に対して、何らかの条件によって、処理が2方向以上に分かれる処理構造を、分岐構造あるいは選択構造と呼びます。C言語で、分岐構造を作るためには if というキーワードを使います(他の方法もあります)。

if文

最も規模が小さい if の使い方は、次のようになります。

if( 条件式 )
    条件を満たす場合に実行する処理;

この全体を指して、if文と呼びます。「if( 条件式 )」の行末にセミコロンは付きません

「条件式」は、例えば「変数num の値が 10 より大きいとき」とか「変数num1 と変数num2 の値が同じとき」といったようなものです。もちろん日本語で書くわけにはいかないので、C言語では次の表にあるような演算子を使います。

演算子 意味
< 左辺が右辺より小さい
> 左辺が右辺より大きい
<= 左辺が右辺以下
>= 左辺が右辺以上
== 左辺と右辺が同じ
!= 左辺と右辺が同じでない

先頭の4つは、大小関係を調べる演算子で、関係演算子と総称されます。うしろの2つは、等しいか等しくないかを調べる演算子で、等価演算子と総称されます。

「左辺と右辺が同じ」であることを、イコール記号を2つつなげて書くことに注意してください。イコール記号1つだけでは代入とみなされてしまいます。なお、文字列の一致を調べる場合には、等価演算子は使えません。これについては、後で説明します

「条件式」の部分に記述した条件を満たしたときにだけ、その直後の文を実行します。条件式が満たされなければ、直後の文は無視して、その先へ進みます。実際に if文を使ったプログラムを見てみましょう。

#include <stdio.h>

int main(void)
{
    char str[40];
    int num;

    puts( "整数を入力してください。" );
    fgets( str, sizeof(str), stdin );
    sscanf( str, "%d", &num );

    if( num >= 100 )
        printf( "100以上\n" );  /* 変数num が 100以上であれば、ここが実行される */

    puts( "終了します。" );     /* ここは常に実行される */

    return 0;
}

実行結果1:

整数を入力してください。
100
100以上
終了します。

実行結果2:

整数を入力してください。
90
終了します。

2回実行して、100以上の値と、100未満の値とを入力して動作を確認してみてください。「100以上」という出力がなされたり、出力されなかったりするはずです。

ここで、「if( num >= 100 )」という条件の反対を考えてみましょう。「num の値が 100以上」の反対なので「num の値が 100以下」と考えがちですが、違います。正しくは「num の値が 100未満」です。

「以上・以下」には「イコール」が含まれていますから、「以上」は「未満」に、「以下」は「より大きい」に置き換えないと、反対の意味にはなりません。

複数の文を実行させたいとき

先程の if文の形式では、条件を満たしたときに実行できる文は1つだけです。2つ以上の文を実行させる場合には、次の形式を取ります。

if( 条件式 ){
    条件を満たす場合に実行する処理1;
    条件を満たす場合に実行する処理2;
    :
}

このように、{ と } を使って複数の文を囲むと、この部分は複合文になります。{ と } の内側にある各行は、先頭に半角空白やタブ文字を入れて、字下げ(インデント)を行い、読みやすくするのが一般的です。

{ を行末に書く人や、{ だけで1行使って書く人がいますが、意味はまったく同じです。

複合文は、複数の文をまとめた1つの塊ですが、中身が1つの文だけでも良く、それどころか中身が空でも問題はありません。このように、複数の文をまとめたものをブロックと呼びます。

{ } を使わない if文は、後から新たな文を追加しようとすると問題になりやすく、バグの原因になることがあります。例えば、変数num の値が負数だったら、符号を反転させて、正数にする if文があるとします。

if( num < 0 )
    num = -num;

ここに後から、標準出力に出力する処理を書き足したとします。

if( num < 0 )
    printf("変数num の値は負数でした。\n");
    num = -num;

{ } がない if文では、「条件を満たす場合に実行する処理」は1文だけが有効です。そのため、上記の if文は、次のものと同じ意味になります。

if( num < 0 ){
    printf("変数num の値は負数でした。\n");
}
num = -num;

このため、条件を満たしても満たさなくても、「num = -num;」の部分が実行されてしまいます。これは非常にありがちなバグです。

このような問題を防ぐため、{ } を使わない if文はやめて、常に { } を使う形式で書くことを奨めます。{ } を使わない方が、記述が少なくて楽に書けるという考え方もありますが、安全な方法を優先した方がいいのは明らかです。とはいえ、{ } を使わずに書く人も多くいるので、他人のプログラムを読むためにも、{ } が使われていない場合があることは知っておく必要があります。

先程のサンプルプログラムを、{ } を使って書きなおすと、次のようになります。

#include <stdio.h>

int main(void)
{
    char str[40];
    int num;

    puts( "整数を入力してください。" );
    fgets( str, sizeof(str), stdin );
    sscanf( str, "%d", &num );

    if( num >= 100 ){
        puts( "100以上" );   /* 変数num が 100以上であれば、ここが実行される */
    }

    puts( "終了します。" );  /* ここは常に実行される */

    return 0;
}

実行結果:

整数を入力してください。
100
100以上
終了します。

条件を満たさないとき

条件を満たさないときにのみ行う処理も記述できます。この場合、次の形式を取ります。

if( 条件式 ){
    条件を満たす場合に実行する処理;
}
else{
    条件を満たさない場合に実行する処理;
}

新たに else というキーワードを導入し、この直後に、条件を満たさない場合の処理を記述します。先ほどと同様に、{ } は中身が1文だけならば省略できますが、やはり個人的には省略しないことを奨めます。

これまでのサンプルプログラムを改造して、100未満のときには「100未満」と表示されるようにしてみます。

#include <stdio.h>

int main(void)
{
    char str[40];
    int num;

    puts( "整数を入力してください。" );
    fgets( str, sizeof(str), stdin );
    sscanf( str, "%d", &num );

    if( num >= 100 ){
        puts( "100以上" );   /* 変数num が 100以上であれば、ここが実行される */
    }
    else{
        puts( "100未満" );   /* 変数num が 100以上でなければ、ここが実行される */
    }

    puts( "終了します。" );  /* ここは常に実行される */

    return 0;
}

実行結果:

整数を入力してください。
99
100未満
終了します。

少し脱線しますが、このサンプルプログラムでは、100 という数値が何度か登場しています。こういう同じ意味の同じ値は、1か所にまとめると良いでしょう。

#include <stdio.h>

int main(void)
{
    char str[40];
    int num;
    int base = 100;		/* 基準値 */

    puts( "整数を入力してください。" );
    fgets( str, sizeof(str), stdin );
    sscanf( str, "%d", &num );

    if( num >= base ){
        printf( "%d以上\n", base );  /* 変数num が基準値以上であれば、ここが実行される */
    }
    else{
        printf( "%d未満\n", base );  /* 変数num が基準値以上でなければ、ここが実行される */
    }

    puts( "終了します。" );  /* ここは常に実行される */

    return 0;
}

実行結果:

整数を入力してください。
99
100未満
終了します。

こうすると、基準となる 100 を別の値に変えたければ、base の値だけを修正すれば良く、変更することが非常に簡単になります。また、一部だけ修正を忘れるといったことがなくなります。

ここでは、変数base を使っていますが、本当は #define(第24章)や、enum(第50章)を使うのが一般的でしょう。しかし、変数を使う方法でも、十分な効果があります。

if文のネスト

「条件を満たす場合の処理」あるいは「条件を満たさない場合の処理」に、さらに if文を書くこともできます。例えば、次のように書きます。

#include <stdio.h>

int main(void)
{
    char str[40];
    int num;

    puts( "整数を入力してください。" );
    fgets( str, sizeof(str), stdin );
    sscanf( str, "%d", &num );

    if( num >= 0 ){
        if( num % 2 == 0 ){
            puts( "正の偶数です。" );
        }
        else{
            puts( "正の奇数です。" );
        }
    }
    else{
        puts( "負の値は対象外です。" );
    }

    return 0;
}

実行結果:

整数を入力してください。
77
正の奇数です。

入力された値が正の値なら、さらに偶数と奇数に応じて分岐しています。このような構造が理解できるようになると、できることの幅が広がります。

偶数と奇数を判定するには、2 で割った余りを調べます。余りがなければ偶数です


文字列の比較

文字列が一致しているかどうか調べる場合、普通に == を使うことはできません。なぜできないのかを現時点で説明することは困難なのでここでは取り上げません(第32章で取り上げます)。

== では正しく比較できないのですが、実際に次のようなプログラムを書くことはできてしまいます。

#include <stdio.h>

int main(void)
{
    char str[6] = "Hello";

    if( str == "Hello" ){
        puts( "一致しています。" );
    }
    else{
        puts( "一致していません。" );
    }

    return 0;
}

実行結果:

一致していません。

こういう書き方はできるのですが、実行結果を見ればわかるように、一致していないと判定されてしまいます。文字列を比較するための正しい方法は、専用の標準ライブラリ関数を使うことです。

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

int main(void)
{
    char str[6] = "Hello";

    if( strcmp( str, "Hello" ) == 0 ){
        puts( "一致しています。" );
    }
    else{
        puts( "一致していません。" );
    }

    return 0;
}

実行結果:

一致しています。

strcmp関数は、実引数に2つの文字列を指定すると、それらを比較して、結果を返します。2つの文字列が一致していれば 0 を返し、1つ目の方が小さければ負数を、1つ目の方が大きければ 0 より大きい値を返します。ここで「小さい・大きい」というのは、辞書順で早く登場する文字が小さいという意味合いになります。

なお、strcmp関数は、<string.h> という場所にあるので、プログラムの冒頭に「#include <string.h>」という新たな行が必要になります

strcmp関数を使って比較を行えば、一致判定を正しく行えます。ただし、戻り値が3通りあるので、一致しているかどうかを調べるなら「== 0」のように比較する必要があることに注意してください。これは、strcmp関数の戻り値が 0 かどうかを調べている訳です。


練習問題

問題① 標準入力から整数を受け取り、その数が3の倍数かどうか判定するプログラムを作成してください。

問題② 標準入力から2つの整数を受け取り、除算を行うプログラムを作成してください。 割る数として 0 が入力されたとき、ゼロ除算をしてしまうことを避け、結果を 0 とするようにしてください。

問題③ 2つの int型の仮引数を持ち、大きい方の値を返すような関数を作成してください。 同様に、小さい方の値を返すような関数も作成してください。


解答ページはこちら

参考リンク



更新履歴

'2018/2/4 全面的に文章を見直し、修正を行った。

'2015/8/22 リンク先が間違っていたので修正。

'2009/3/17 「この章の概要」を追加。

'2009/2/28 新規作成。



前の章へ (第10章 関数プロトタイプ)

次の章へ (第12章 多方向へ分岐させる)

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

Programming Place Plus のトップページへ


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