while文 ループ構文2

while文

C言語ではfor文のほかに、while文というループ文があります。

#include <stdio.h>

int main()
{
    int i = 0;
    while(i < 10)
    {
        printf("%d ", i);
        i++;
    }
    getchar();
}
0 1 2 3 4 5 6 7 8 9 
while(条件式){ 文 }
条件式が真の間、文を繰り返し実行する

while文で使用するのは条件式ひとつだけです。
条件式が真の間、文が実行されます。

while文はfor文に比べると簡潔にループを書くことができます。
ただ、サンプルコードのような場合は終了条件判定に変数(ループカウンタ)を使用しています。
このような場合はfor文を用いたほうがいいでしょう。

意図的な無限ループ

while文は意図的に無限ループをさせるコードでよく用いられます。

#include <stdio.h>

int main()
{
    int count = 0;
    while (1)
    {
        if (count < 10)
        {
            printf("%d ", count);
            count++;
            continue;
        }
        break;
    }
    getchar();
}
0 1 2 3 4 5 6 7 8 9 

while文の条件判定に「1」が指定されています。
C言語の条件判定では0は偽、それ以外は真となります。
条件判定に1を指定すると常に真となるので、このwhile文は無限ループとなります。

本当に無限ループにしてしまうとプログラムが終了できませんので、ループ文を抜けるための処理を必ず記述します。

continue文

サンプルコードでは、変数countの値が10未満の間は変数countの値を画面に表示し、値をひとつ増やしています。
次にcontinue文を実行します。
continue文は処理をループ文の先頭に戻します。
continue文が実行されると、その行以降の処理はスキップされループ文の先頭に戻ります。

ループにより変数countが10以上になると、if文は実行されません。
その次のbreak文により、ループ処理を抜けます。
break文はswitch文の時にも説明しましたが、現在のswitch文やループ文を抜けます。

ループ文とgetchar関数

getchar関数は標準入力(コンソール画面)からキーボード入力を受け取る関数ですが、この関数をきちんと使用するにはループ文と組み合わせる必要があります。

#include <stdio.h>

int main()
{
    while(1)
    {
        printf("プログラムを終了しますか?\n");
        printf("y=終了 n=キャンセル\n");
        int c = getchar();

        if (c == 'y')
        {
            break;
        }
        printf("終了をキャンセルしました。\n\n");
        while (getchar() != '\n');
    }
    while (getchar() != '\n');
    printf("何かキー入力で終了。\n");
    getchar();
}

このコードはキーボードで「y」を入力するとプログラムを終了することができます。
y以外を入力すると再度プログラムの終了確認に戻ります。

プログラムを終了しますか?
y=終了 n=キャンセル
a
終了をキャンセルしました。

プログラムを終了しますか?
y=終了 n=キャンセル
y
何かキー入力で終了。
int getchar()
標準入力から文字を受け取る

getchar関数は、標準入力から文字をひとつだけ読み取る関数です。
入力はキーボードと確定しているわけではありませんが、特別なことをしない限り普通はキーボードです。

反対に、printf関数などは「出力」側の関数です。
ディスプレイへの出力を標準出力といいます。
これもディスプレイと確定しているわけではありませんが、通常はディスプレイです。

戻り値はint型です。
何らかのエラーが発生した場合は負数(マイナス値)を返します。

文字はchar型で扱うのですが、getchar関数はint型で返します。
データ型の項ではchar型は「-128~127」と説明しましたが、char型は文字を扱うために特別な扱いになっていることがあります。
その場合、char型は「0~255」の範囲となり、負数は扱えません。
そのため、確実に負数を扱えるint型で返すようになっています。

標準入力

getchar関数は「キー入力待ちを発生させる」関数ではなく「標準入力から文字をひとつ読み取る」関数です。
getchar関数を実行するとキーの入力待ちになりますが、これは標準入力が空の場合のみに発生する動作です。

getchar関数を実行し、「abc」と入力してEnterキーを押下したとします。
この時、標準入力には「abc\n」の文字列が入力された状態となります。
(\nは最後のEnterキーによる改行文字。エスケープシーケンスを参照)
標準入力は入力されたデータを一時的に保存しておく機能があります。

getchar関数は、この文字列から先頭の「a」を取り出して返します。
残りの「bc\n」は標準入力に残ったままとなります。

次にgetchar関数を実行すると、「bc\n」から「b」を取り出して返します。
この時、キーの入力待ちは発生しません。
getchar関数の実行を繰り返して標準入力を空にし、さらにgetchar関数を実行することでようやく入力待ちが発生します。

この動作を理解していないとうまくキー入力を受け取ることができません。
例えば以下のコードで「abc」と入力する場合、Enterキーを押した瞬間にプログラムが終了してしまいます。

#include <stdio.h>

int main()
{
	int c;

	c = getchar();
	printf("%c\n", c);

	getchar();
}

最初のgetchar関数で「a」を変数に受け取りますが、次のgetchar関数の実行時は標準入力から「b」を読み取るため、キーの入力待ちが発生しません。
そのため、画面を確認する暇もないままプログラムが終了してしまうのです。

改行までを読み取る

ユーザーがプログラム実行時に何文字を入力するかはプログラム作成時に知ることはできません。
入力された文字が何文字だろうと正常に読み取るにはループ文が必要になります。

getchar関数はEnterキーを入力することで処理を勧めますから、入力された文字の末尾は確実に「\n」です。
つまりループ文で「\n」が出現するまで読み取ってやれば良いのです。

#include <stdio.h>

int main()
{
    int c;
    
    c = getchar();
    printf("%c\n", c);
    
    //¥nが出現するまで読み捨てる
    while (getchar() != '\n');
    
    getchar();
}

こうすれば何文字入力されようとも入力された文字列の先頭の文字を表示し、さらにプログラムを終了することなくキーの入力待ちを発生させることができます。
つまりこれは標準入力のデータをクリアする処理ということです。

ただし上記コードは、何も入力せずにEnterキーが押下されたときの動作は考慮していません。
詳細は次ページで説明します。

while文の条件式の処理が若干わかりにくいと思うので説明します。

while (getchar() != '\n');
  1. getchar関数が実行されます。
  2. 読み取った文字と「\n」が一致するか否かを判定します。
    「!=」なので一致しなかった場合は真となります。
  3. 真ならばwhile文が実行されますが、このwhile文には実行する文がないのですぐに1に処理が戻ります。
  4. getchar関数が「\n」を返すと条件式が偽になるのでwhile文が終了します。
  5. while文終了後、標準入力は「\n」も含めてすべて読み取られた状態となります。

ちなみに、文字を読み捨てずに変数に格納する場合は例えば以下のようにします。

#include <stdio.h>

int main()
{
	int c;

	while ((c = getchar()) != '\n')
		printf("%c", c);

	getchar();
}

getchar関数の実行後、読み取った値を変数cに代入しています。
代入式を評価すると代入された値が得られるので、それを利用して「\n」と一致するか否かを判定します。
一致しなかった場合はprintf関数で文字を画面に表示しています。