🔀 制御構造
🎯 二重ループの動きを説明でき、九九表のような表形式の出力を自分で組み立てられる
アナログ時計を見てください。長針が1周する間に、短針は1目盛りだけ進みます。外側が1回進む間に、内側は最初から最後まで回りきる。これがループのネスト(入れ子)です。
外側3回、内側3回なら、本体が実行されるのは 3×3 = 9回。掛け算になるのがポイントです。
定番中の定番、九九表を作りましょう。
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 9; i++) { // 段
for (int j = 1; j <= 9; j++) { // 掛ける数
printf("%3d", i * j);
}
printf("\n"); // 1段ぶん終わったので改行
}
return 0;
}9行9列の表が出ます。%3d は「3桁ぶんの幅で右寄せ」というprintfの書式指定で、これがあるおかげで桁が揃います。
このコードで本当に大事なのは、改行の位置です。printf("\n") は内側のループの外、外側のループの中にあります。ここを間違えると形が崩れます。
改行を内側のループの中に入れると、数字が1つ出るたびに改行されて縦1列になります。改行を外側のループの外に出すと、81個の数字が全部1行に並びます。「内側が回りきったら、1行ぶん終わり」——この対応を意識すると、表の出力で迷わなくなります。
内側の条件に外側の変数を使うと、段ごとに長さを変えられます。
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) { // 上限が外側の i
printf("*");
}
printf("\n");
}
return 0;
}* が1個、2個、3個…と増えて三角形になります。ネストの本当の威力はここで、内側と外側を連動させると一気に表現力が上がります。
前のトピックで触れた「breakは内側しか抜けない」問題も片付けておきましょう。外側まで抜けたいときはboolのフラグを使います。
#include <stdio.h>
int main(void) {
bool found = false;
for (int i = 1; i <= 3 && !found; i++) { // フラグを外側の条件にも入れる
for (int j = 1; j <= 3; j++) {
if (i * j == 4) {
printf("見つけた: %d x %d\n", i, j);
found = true;
break; // 内側を抜ける
}
}
}
return 0;
}内側で found = true にしてから break すると、外側の継続条件 !found が偽になって外側も終わります。少し回りくどく見えますが、流れが1本道なので後から読む人に優しい書き方です。
罠を2つ。
その1、カウンタ名の使い回し。 外側も内側も i にすると、内側が外側のカウンタを壊して動きがめちゃくちゃになります。i, j, k と分けてください。
その2、実行回数は掛け算。 外側 n 回・内側 n 回なら本体は n×n 回です。n が 1000 なら100万回。ネストは静かに重くなります。三重にすれば n×n×n。「入れ子を1つ増やすと桁が変わる」という感覚は、今のうちに持っておいて損はありません。
まとめます。外側1周につき内側は回りきる。表の形を決めるのは改行の位置。カウンタ名は分ける。実行回数は掛け算で効いてくる。
if文やループの中に、さらにif文やループを書くことのこと。九九表のような2次元的な処理を扱うときによく使われる書き方。深くなりすぎると読みにくくなるため、関数分割で整理することもある。
「初期化・条件・更新」の3つを1行にまとめて書ける繰り返し構文のこと。回数があらかじめ決まっている繰り返しにとくに向いている。繰り返す回数があらかじめはっきりしている場面で、とくに書きやすい構文。
ループやswitch文の実行をその場で強制的に抜け出す命令のこと。「これ以上繰り返す必要がない」と判断したときに使う。多重ループの中で使うと、いちばん内側のループだけを抜ける点に注意が必要。
true(真)かfalse(偽)のどちらかしか入らない型のこと。C23では標準の書き方としてbool・true・falseがそのまま使えるようになった。従来は0と1で真偽を表していたが、C23の書き方の方が意図が伝わりやすい。
もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。