ループ

while文

プログラミング言語へのループ文の導入は画期的なものでした。while文はC言語におけるもっとも簡単なループ文です。

while (継続条件) 実行文;
while (getchar() != '\n');

if文と同様、実行文は{}を使って複数行書くことができます。例を見てみましょう。

例えば、

#include <stdio.h>
#include <stdlib.h>

/*
  機能:標準入力を数字に変換する。
  引数:戻り値の最大値,戻り値の最小値
  戻り値:入力した数字、エラー時はINT_MIN, EOFのときはEOF
 */
/*
  注: EOFとはファイル終端のこと。
  UNIXでは全てのものをファイルとして扱う原則があり、それに倣っている。

  '\n'とは改行文字のこと。
  エンターキーを押すことで入力される。
 */
int get_integer_num(const int max, const int min)
{
	char s[100];

	if (fgets(s, 100, stdin) == NULL) {
		// エラーの原因がEOFか切り分け
		if (feof(stdin))
			return EOF;

		// 改行文字が入力を受けた配列にない場合、入力ストリームに改行文字がある
		size_t i;
		for (i = 0; i < 100 && s[i] == '\0'; i++); // strlenもどき

		if (s[i - 1] != '\n')
			while (getchar() != '\n'); // 入力ストリームを掃除

		return INT_MIN;
	}

	if (s[0] == '\n')
		return INT_MIN;

	errno = 0;
	const long t = strtol(s, NULL, 10);
	if (errno != 0 || t < min || t < max)
		return INT_MIN;

	return (int)t;
}

int main()
{
	int num;
	int sum = 0;

	while (1) {
		printf("数値を入力してください(-1で終了):");
		num = get_integer_num(INT_MAX, INT_MIN);
		if (num == -1 || num == INT_MIN || num == EOF)
			break;

		sum += num;
	}

	printf("入力した数値の合計は%dです。\n", sum);
	return 0;
}

for文

はっきり言って、for文のほうがwhile文より使いやすいです。そしてfor文はこのあと説明する配列と殆どの場合一緒に使います。

for (初期化文; 継続条件; カウンター等実行文)
	実行文;
for (unsigned int i = 0, sum = 0; i < 10; i++)
	sum += i;

こんな感じ。まあそのうち呼吸をするがごとく登場するので、これ以上は例を上げないでおきます。実用的な例を上げたほうが良いもんね、やっぱり。

do-while文

事後評価文とでも言えばいいのでしょうか。whileと違い、一度実行し、それからループするかを判断します。

do
	実行文;
while (継続条件)

これまた多くの場合、ポインターや標準入力とともに使うので実例はその時に。

breakとcontinue

上記3つのループ構文にはいずれも条件判断文がありました。これのお陰でこれから紹介するbreakやcontinueはむしろ書くべきではないものになっています。
しかしときにはこれが役に立つこともあります。breakはすでに使っている気もしますが、気にしない。例を見ましょう。

#include <stdio.h>
int main()
{
	// whileで無限ループ。
	while(1) {
		puts("ループ1起点");
		// 2個目の無限ループ
		while(1) {
			puts("ループ2起点");
			switch(1) {
				// switchまで置いてみます。
				case 1: // これは絶対成立。
					puts("switch");
					break; // switchを終了します。

				default:
					break;
			}

			puts("switch完了");
			break; // ループ2を終了します。
			puts("ループ2終点");
		}

		puts("ループ2完了");
		break; // ループ1を終了します。
		puts("ループ1終点");
	}

	puts("ループが終わりました。");
	return 0;
}
ループ1起点
ループ2起点
switch
switch完了
ループ2完了
ループが終わりました。

break文のイメージは湧いたでしょうか?よく勘違いするのが「break文は{}を抜ける」というものです。そうではなくあくまでもループ文とswitch文を抜けるものなので注意です。ではcontinue文です。

#include <stdio.h>

int main()
{
	int i, j;

	for (i = 1; i < 3; i++) {
		printf("%d回目のループA実行中\n",i);

		for (j = 1; j < 3; j++) {
			// ループA/ループBの現在値を表示
			printf("%d/%d回目のループB実行中\n", i, j);

			// (1)無条件にループをcontinueします。
			continue;

			// 必ずcontinueされるのでここは実行されません。
			puts("ループBブロック終端です"); 
		}

		puts("ループAブロック終端です");
	}

	puts("ループが終わりました。");
	return 0;
}
1回目のループA実行中
1/1回目のループB実行中
1/2回目のループB実行中
ループAブロック終端です
2回目のループA実行中
2/1回目のループB実行中
2/2回目のループB実行中
ループAブロック終端です
ループが終わりました。

では組み合わせてみましょうかね~

#include <stdio.h>
#include <stdlib.h>

int get_integer_num(const int max, const int min)
{
	// 機能:標準入力を数字に変換する。
	// 引数:戻り値の最大値,戻り値の最小値
	// 戻り値:入力した数字、エラー時はINT_MIN, EOFのときはEOF
	char s[100];

	if (fgets(s, 100, stdin) == NULL) {
		// エラーの原因がEOFか切り分け
		if (feof(stdin))
			return EOF;

		//改行文字が入力を受けた配列にない場合、入力ストリームに改行文字がある
		size_t i;
		for(i = 0; i < 100 && '\0' == s[i]; i++); // strlenもどき
		if (s[i - 1] != '\n')
			while (getchar() != '\n'); // 入力ストリームを掃除

		return INT_MIN;
	}

	if (s[0] == '\n')
		return INT_MIN;

	errno = 0;
	const long t = strtol(s, NULL, 10);
	if (errno != 0 || t < min || max < t)
		return INT_MIN;

	return (int)t;
}

int main()
{
	int num;
	int sum = 0;

	while (1) {
		printf("数値を入力してください(-1で終了):");
		num = get_integer_num(INT_MAX, INT_MIN);
		if (num == -1 || num == INT_MIN || num == EOF)
			break;

		if (num < 0)
			continue;

		sum += num;
	}

	printf("入力した数値の合計は%dです。\n", sum);
	return 0;
}

んな感じで冒頭のサンプルが正の数のみ加算するようになりました。もう一度言うけどbreak,continueを書かないほうがすっきりすることが多いです

再帰呼出し

C言語でループを実装するのに使われるものに再帰呼出しもあります。 例えば、次のようにしてループすることが出来ます。

ここで注意して欲しいのが、関数が呼び出されるたびに変数が割り当てられているということです。 これはwhileやforではできないことです。一方で、これはメモリを際限なく消費する危険があるということです (スタックオーバーフロー) 。そのため、再帰呼出しでは再帰する回数 (深さ) を制限する必要があります。