C言語の基本|C言語の制御文

条件で分け、必要なだけ繰り返し、適切な場所で流れを変える。C言語の制御文を使って、アルゴリズムを動くプログラムに変えよう。

プログラムの基本的な流れは、順次、選択、繰り返しという3つの構造で表せます。

順次構造では、記述された文を上から順番に実行します。選択構造では、条件によって実行する処理を分けます。繰り返し構造では、同じ処理を決められた回数や条件に従って反復します。

C言語には、これらの流れをコードで表現するための制御文が用意されています。

たとえば、次のような処理を作るときに制御文を使います。

  • 入力された点数によって合否を判定する
  • メニュー番号によって実行する機能を切り替える
  • 配列の要素を先頭から順番に処理する
  • 特定の値が入力されるまで処理を繰り返す
  • 条件を満たしたら繰り返しを終了する
  • 不要なデータを飛ばして次の処理へ進む

制御文を使うと、プログラムが状況に応じて実行する処理を選べるようになります。

単純に上から下へ実行するだけでなく、どこで分岐し、いつまで繰り返し、どの時点で処理を抜けるのかを指定できるようになります。

制御文とは

制御文は、プログラムの実行順序を変えるための文です。

C言語の文は、基本的に上から下へ順番に実行されます。しかし、すべての処理を一度ずつ実行するだけでは、条件判定や繰り返しのあるプログラムを作れません。

そこで、制御文を使って処理の進む方向を変えます。

制御したい流れ使用する制御文の例
条件によって処理を選ぶif、switch
同じ処理を繰り返すfor、while、do-while
繰り返しやswitchを途中で終了するbreak
今回の処理を飛ばして次の繰り返しへ進むcontinue
指定した位置へ移動するgoto
関数の処理を終了するreturn

制御文を使うことで、流れ図で表した選択や繰り返しをC言語のコードに置き換えられます。

C言語の制御文は3つに分類できる

C言語の制御文は、選択文、繰り返し文、ジャンプ文の3つに分けて考えられます。

分類制御文主な役割
選択文if、switch条件や値に応じて処理を選ぶ
繰り返し文for、while、do-while条件や回数に従って処理を反復する
ジャンプ文break、continue、goto、return現在の流れを途中で変更する

選択文は、選択構造をC言語で表すために使います。

繰り返し文は、繰り返し構造を表します。繰り返す回数や終了条件に応じて、for、while、do-whileを使い分けます。

ジャンプ文は、現在実行している流れから別の位置へ移動するために使います。繰り返しから抜けたり、次の繰り返しへ進んだり、関数そのものを終了したりできます。

図1:C言語の制御文を3つに分類

この図から分かること

C言語の制御文は、目的によって3つに分類できます。

条件によって処理を選ぶ場合はifやswitchを使います。同じ処理を繰り返す場合はfor、while、do-whileを使います。

繰り返しの途中で終了したり、今回の処理を飛ばしたりするときは、breakやcontinueなどのジャンプ文を使います。

どの制御文を使うか迷ったときは、分岐したいのか、繰り返したいのか、現在の流れを途中で変更したいのかを考えると選びやすくなります。

ifは条件によって処理を分ける

ifは、条件が真の場合だけ特定の処理を実行する選択文です。

たとえば、音量が設定された上限を超えている場合だけ警告を表示する処理に使えます。

ifの条件が真なら、ifに続く処理が実行されます。条件が偽なら、その処理は実行されません。

条件が真の場合と偽の場合で、それぞれ異なる処理を実行したいときはif elseを使います。

条件の結果実行される処理
if側の処理
else側の処理

ifでは、比較演算子や論理演算子を使って条件式を作ります。

条件式の例判定する内容
level >= 80levelが80以上か
temperature < 40temperatureが40未満か
count == 0countが0と等しいか
value != -1valueが-1と異なるか

C言語では、条件式の結果が0なら偽、0以外なら真として扱われます。

ifを複数の条件に対応させる

2方向だけでなく、複数の条件に分けたい場合は、if、else if、elseを組み合わせます。

たとえば、入力された音量レベルを次の3段階に分ける場合です。

  • 80以上なら高い
  • 40以上80未満なら標準
  • 40未満なら低い

条件は上から順番に判定されます。最初に真になった処理だけが実行され、それ以降の条件は判定されません。

そのため、条件を書く順番も重要です。

80以上を判定する前に40以上を判定すると、80以上の値も40以上の条件で真になってしまいます。範囲を分けるときは、条件が重ならないように順番を考えます。

switchは値によって多方向に分岐する

switchは、1つの式の値に応じて、複数の処理から実行するものを選ぶ制御文です。

たとえば、操作メニューを次のように分ける場合に向いています。

メニュー番号実行する処理
1音量設定を表示する
2入力チャンネルを表示する
3音響効果を表示する
4設定画面を終了する

switchでは、caseを使って値ごとの処理を記述します。どのcaseにも一致しなかった場合の処理はdefaultに記述できます。

多くの場合、各caseの最後にはbreakを置きます。breakがないと、一致したcaseより後ろの処理まで続けて実行される場合があるためです。

ifとswitchの使い分け

比較する内容向いている選択文
数値の大小関係if
複数の条件を組み合わせた判定if
範囲による判定if
1つの整数値による分岐switch
メニュー番号による分岐switch
複数の決まった値から選ぶswitch

ifは、以上、未満、等しくないなどのさまざまな条件を表現できます。

switchは、1つの式の値がどのcaseと一致するかによって処理を選びます。メニュー番号のように、分岐する値がはっきり決まっている場合に読みやすくなります。

繰り返し文は同じ処理を反復する

同じ処理を何度も実行するときは、繰り返し文を使います。

C言語では、for、while、do-whileという3つの繰り返し文が用意されています。

繰り返し文条件を判定する位置主な用途
for処理を実行する前回数が決まっている処理
while処理を実行する前条件が真の間だけ続ける処理
do-while処理を実行した後最低1回は実行する処理

繰り返し文を選ぶときは、繰り返す回数が分かっているか、条件をいつ判定するかに注目します。

forは回数が決まっている繰り返しに向いている

forは、指定した回数だけ処理を繰り返す場合に向いています。

たとえば、次のような処理です。

  • メッセージを10回表示する
  • 配列の5個の要素を順番に処理する
  • 1から100までの合計を求める
  • 5回分の測定値を入力する

forでは、繰り返しに使用する変数の初期化、継続条件、更新を1か所に記述できます。

forを構成する要素役割
初期化繰り返しに使う変数の最初の値を設定する
条件式繰り返しを続ける条件を判定する
更新1回の処理が終わるたびに値を変化させる

回数を管理する処理がまとまっているため、繰り返し回数を確認しやすいのが特徴です。

whileは条件が真の間だけ繰り返す

whileは、処理を実行する前に条件を判定する前判定の繰り返し文です。

条件が真なら処理を実行し、処理が終わると再び条件を判定します。条件が偽になった時点で繰り返しを終了します。

whileは、次のような処理に向いています。

  • 終了を表す値が入力されるまで続ける
  • 正しい値が入力されるまで再入力する
  • データが残っている間だけ処理する
  • 条件が成立している間だけ監視を続ける

最初の条件判定が偽だった場合は、whileの中の処理が一度も実行されないことがあります。

do-whileは処理を最低1回実行する

do-whileは、処理を実行した後で条件を判定する後判定の繰り返し文です。

最初に処理を実行してから条件を判定するため、条件の結果にかかわらず、処理部分が最低1回は実行されます。

do-whileは、次のような処理に向いています。

  • メニューを最低1回表示する
  • 値を入力してから続けるか確認する
  • 最初の処理後に再実行するか選択する

do-whileでは、最後のwhileの条件式に続くセミコロンを忘れないようにします。

図2:for・while・do-whileの違い

この図から分かること

forは、繰り返す回数が決まっている処理に向いています。配列の要素を先頭から順番に処理する場合にもよく使われます。

whileは、処理を実行する前に条件を判定します。最初から条件が偽なら、処理は一度も実行されません。

do-whileは、処理を実行した後で条件を判定します。そのため、処理部分が最低1回は実行されます。

繰り返し文を選ぶときは、回数が決まっているか、最初の処理前に条件を判定する必要があるかを考えます。

ジャンプ文は現在の流れを途中で変更する

ジャンプ文は、通常の実行順序を途中で変更するための制御文です。

ジャンプ文主な役割使用場面
break現在のループやswitchを終了する終了条件を満たしたとき
continue今回の残りの処理を飛ばす対象外のデータを無視するとき
goto指定したラベルへ移動する特別な理由で直接移動するとき
return現在の関数を終了する結果を呼び出し元へ返すとき

ジャンプ文は便利ですが、使う場所によっては処理の流れを追いにくくします。特にgotoは、移動先が増えるとコードの読みやすさが低下しやすいため、学習段階では存在と基本的な役割を押さえておけば十分です。

breakは繰り返しやswitchを終了する

breakは、現在実行している繰り返しやswitchを終了します。

繰り返しの中でbreakが実行されると、ループの後ろにある処理へ進みます。

代表的な使用場面は次のとおりです。

  • 終了を表す値が入力された
  • 目的のデータが見つかった
  • エラーが発生して処理を続けられない
  • switchで一致するcaseの処理が終わった

繰り返しが二重になっている場合、breakが終了するのは、breakが直接含まれている最も内側の繰り返しです。

continueは今回の残りを飛ばす

continueは、繰り返しそのものを終了するのではなく、現在の1回分に残っている処理を飛ばします。

その後、次の繰り返しへ進みます。

たとえば、複数の数値から負の値だけを無視する場合に使えます。

  • 正の値なら合計へ加える
  • 負の値ならcontinueで加算を飛ばす
  • 繰り返しはそのまま続ける

breakは繰り返し全体を終了しますが、continueは今回の処理だけを途中で終わらせます。

gotoは指定したラベルへ移動する

gotoは、同じ関数内にある指定したラベルへ処理を移動させます。

自由に移動できるため、複雑な使い方をすると、処理がどこからどこへ進むのか分かりにくくなります。

順次、選択、繰り返しを使えば多くの処理を表現できるため、最初のうちはgotoを積極的に使う必要はありません。

returnは関数を終了する

returnは、現在実行している関数を終了し、呼び出し元へ処理を戻します。

main関数のreturn 0;は、プログラムが正常に終了したことを表します。

値を返す関数では、returnに続けて戻り値を指定します。returnが実行されると、それより後ろに記述されている同じ関数内の処理は実行されません。

選択文・繰り返し文・ジャンプ文を組み合わせる

実際のプログラムでは、1種類の制御文だけでなく、複数の制御文を組み合わせます。

ここでは、音響機器の検査台数を繰り返し入力し、有効な値だけを合計します。

処理の流れは次のとおりです。

  1. 合計を0で初期化する
  2. 検査台数を入力する
  3. -1ならbreakで入力を終了する
  4. -1以外の負の値ならcontinueで無視する
  5. 0以上なら合計に加える
  6. 再び入力処理へ戻る
  7. 繰り返しが終了したら合計を表示する

この処理では、while、if、break、continueを組み合わせます。

図3:複数の制御文を組み合わせた処理の流れ

この図から分かること

whileによって、検査台数の入力を繰り返します。

入力値が-1ならbreakを実行し、whileを終了して合計表示へ進みます。

-1以外の負の値は入力エラーとして扱います。この場合はcontinueを実行し、加算を行わずに次の入力へ戻ります。

0以上の値なら、合計へ加算して次の入力へ戻ります。

breakは繰り返し全体を終了しますが、continueは今回の加算だけを飛ばして繰り返しを続けます。

制御文を組み合わせたプログラム

検査台数を繰り返し入力して有効な値だけを合計するプログラム

ファイル名:7_5_1.c

#include <stdio.h>

int main(void)
{
    int inspected_count;
    int total_count = 0;

    printf("検査台数を入力してください。\n");
    printf("-1で入力を終了します。\n");
    printf("-1以外の負の値は合計しません。\n");

    while (1) {
        printf("検査台数 > ");
        scanf("%d", &inspected_count);

        if (inspected_count == -1) {
            break;  /* -1なら繰り返しを終了する */
        }

        if (inspected_count < 0) {
            continue;  /* 負の値は加算せず次の入力へ進む */
        }

        total_count += inspected_count;
    }

    printf("有効な検査台数の合計は%d台です。\n", total_count);
    printf("複数の制御文を組み合わせて処理しました。\n");

    return 0;
}
実行結果の例
検査台数を入力してください。
-1で入力を終了します。
-1以外の負の値は合計しません。
検査台数 > 12
検査台数 > -4
検査台数 > 8
検査台数 > -1
有効な検査台数の合計は20台です。
複数の制御文を組み合わせて処理しました。
プログラムの詳しい解説

このプログラムでは、while、if、break、continueを組み合わせています。

記述分類役割
while (1)繰り返し文breakが実行されるまで入力を繰り返す
if (inspected_count == -1)選択文入力終了の値か判定する
break;ジャンプ文whileを終了する
if (inspected_count < 0)選択文無効な負の値か判定する
continue;ジャンプ文加算を飛ばして次の入力へ進む
total_count += inspected_count;順次処理有効な検査台数を合計する
return 0;ジャンプ文main関数を終了する

inspected_countには、1回分の検査台数を保存します。

total_countには、有効な検査台数の合計を保存します。加算を始める前に0で初期化しています。

whileの条件には1を指定しています。C言語では0以外が真なので、whileの条件は常に真になります。そのため、breakが実行されるまで入力を繰り返します。

最初のifでは、入力値が-1と等しいかを判定します。-1ならbreakが実行され、whileの後ろにある合計表示へ進みます。

2つ目のifでは、入力値が0より小さいかを判定します。ただし、-1は最初のifで処理されるため、ここでは-4などの無効な負の値が対象になります。

負の値ならcontinueが実行されます。continueより後ろにある加算処理は実行されず、whileの先頭へ戻ります。

入力値が-1でも負の値でもなければ、total_countへ加算されます。

実行結果の値は、次のように処理されます。

入力値実行される制御total_count
12合計に加算する12
-4continueで加算を飛ばす12
8合計に加算する20
-1breakでwhileを終了する20

-4はcontinueによって無視されるため、合計には入りません。-1は入力終了を表すため、breakによって繰り返しを終了します。

breakとcontinueの違い

比較項目breakcontinue
繰り返し全体終了する終了しない
今回の残りの処理実行しない実行しない
次の繰り返し進まない進む
ループ後の処理すぐに進むループ終了後に進む
主な用途終了条件を満たした今回のデータだけ無視したい

breakを実行すると、現在の繰り返しから完全に抜けます。

continueを実行すると、今回の繰り返しに残っている処理だけを飛ばします。繰り返し自体は終了しないため、次のデータを処理できます。

制御文を使うときの注意点

条件式の境界を確認する

以上とより大きい、以下とより小さいでは、境界値の判定が異なります。

30以上ならbattery_level >= 30、30より大きいならbattery_level > 30です。

条件を作るときは、境界の値をどちらの処理に含めるのか確認します。

繰り返しの終了条件を用意する

whileやdo-whileでは、条件がいつか偽になるように値を更新する必要があります。

while (1)のように常に真となる条件を使う場合は、breakが確実に実行される終了経路を用意します。

終了条件がなければ、処理が終わらない無限ループになる可能性があります。

制御文の範囲を波括弧で明確にする

ifやforなどで実行する文が1つだけなら、波括弧を省略できる場合があります。

ただし、処理を追加したときの間違いを防ぎ、制御される範囲を分かりやすくするためにも、初心者のうちは波括弧を付けて記述すると安全です。

制御文を深く重ねすぎない

ifや繰り返しを何重にも重ねると、処理の流れを追いにくくなります。

複雑になった場合は、次の点を見直します。

  • 条件を整理できないか
  • continueやreturnで早めに不要な処理を終えられないか
  • まとまった処理を別の関数に分けられないか
  • 変数名から条件の意味が分かるか

制御文は流れを柔軟にする道具ですが、使い方が複雑になると読みやすさが低下します。どこで分岐し、どこへ戻り、どの条件で終了するのかが分かる形にすることが大切です。