決まった回数だけ同じ処理を繰り返す場面では、C言語のfor
文がもっとも読みやすく、意図が伝わりやすい選択です。
初期化→条件→更新という3つの要素を1行にまとめられるため、ループの範囲や回数が一目でわかります。
本記事では、初心者の方がつまずきやすい順番やスコープ、書き方のコツ、典型的なミスまで丁寧に解説します。
C言語のfor文の基本構文と使い方
構文と各部の名前(初期化 条件 更新)
for文は「初期化」「条件」「更新」の3部でヘッダを構成し、続くブロック(本体)を繰り返します。
もっとも基本的な形は次のとおりです。
for (初期化; 条件; 更新) {
// 本体(繰り返したい処理)
}
実例とともに各部の役割を見てみます。
#include <stdio.h>
int main(void) {
// iを0から始めて、iが3未満の間、iを1ずつ増やす
for (int i = 0; i < 3; i = i + 1) { // 更新は i = i + 1 と書けます
printf("i = %d\n", i); // 本体
}
return 0;
}
i = 0
i = 1
i = 2
下の表は、各部の意味と実行タイミングをまとめたものです。
部分 | 例 | 役割 | 実行タイミング |
---|---|---|---|
初期化 | int i = 0 | ループ用の変数や状態の初期設定 | ループ開始時に1回だけ |
条件 | i < 3 | 続けるか止めるかを決定 | 各反復の先頭(本体に入る前) |
更新 | i = i + 1 | 次の反復へ進めるための変更 | 各反復の末尾(本体の後) |
本体 | printf(...) | 実際に繰り返したい処理 | 条件が真のときに実行 |
3つの式のいずれかは省略可能ですが、省略した場合は意図が伝わりにくくなることがあります。
初心者のうちは省略を避け、明示的に書くと読みやすくなります。
実行順序(初期化→条件→本体→更新)
for文は必ず「初期化→条件→本体→更新」の順に動きます。
次のプログラムは、各段階でメッセージを出力して順序を確認します。
#include <stdio.h>
// 補助関数: メッセージを出して値を返す
int init(void) { printf("初期化\n"); return 0; }
int cond(int i) { printf("条件判定(i=%d)\n", i); return i < 2; }
int update(int i){ printf("更新(i=%d→%d)\n", i, i + 1); return i + 1; }
int main(void) {
int i;
for (i = init(); cond(i); i = update(i)) {
printf("本体(i=%d)\n", i);
}
printf("ループ終了\n");
return 0;
}
初期化
条件判定(i=0)
本体(i=0)
更新(i=0→1)
条件判定(i=1)
本体(i=1)
更新(i=1→2)
条件判定(i=2)
ループ終了
この順序を理解すると、なぜ本体の最後に処理を書いたのに次の反復で効いているのかといった混乱が減ります。
ループ変数の宣言位置とスコープ
C99以降ではfor (int i = 0; ...)
のように、ループ変数をfor文の中で宣言できます。
この場合、変数i
の有効範囲(スコープ)はfor文の内部だけです。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 3; i = i + 1) {
printf("%d ", i);
}
// printf("%d\n", i); // エラー: iはここでは見えません
printf("\n(ループの外ではiは無効)\n");
return 0;
}
0 1 2
(ループの外ではiは無効)
一方、ループの外で宣言すると、for
の後でも使えます。
用途に応じて選びますが、変数の寿命を最小化するため、可能ならforの中で宣言するのが安全です。
#include <stdio.h>
int main(void) {
int i; // 外で宣言
for (i = 0; i < 3; i = i + 1) {
printf("%d ", i);
}
printf("\nforの外でもi=%dを参照できます\n", i); // ここでは見える
return 0;
}
0 1 2
forの外でもi=3を参照できます
条件式の評価(0/非0)
C言語では条件式が0なら偽(false)、0以外なら真(true)として扱われます。
たとえば-5
も真です。
#include <stdio.h>
int main(void) {
if (0) {
printf("これは表示されません\n");
} else {
printf("0は偽です\n");
}
if (42) {
printf("42は真です\n");
}
if (-5) {
printf("-5も真です\n");
}
return 0;
}
0は偽です
42は真です
-5も真です
注意: 条件式に計算結果を直接書くときは、0と非0の扱いを意識しましょう。
意図しない非0が「真」になってループが止まらないことがあります。
for文の書き方のコツ
0からN未満で回す
配列の走査では0からN
未満が基本です。
境界を超えにくく、標準的な書き方です。
#include <stdio.h>
int main(void) {
int N = 5;
for (int i = 0; i < N; i = i + 1) {
printf("%d ", i);
}
printf("\n");
return 0;
}
0 1 2 3 4
1からNまで回す
数学的な定義に合わせたい場合は、1
からN
まで回します。
#include <stdio.h>
int main(void) {
int N = 5;
int sum = 0;
for (int i = 1; i <= N; i = i + 1) {
sum = sum + i;
}
printf("1から%dまでの合計は%dです\n", N, sum);
return 0;
}
1から5までの合計は15です
逆順に回す
逆順はN - 1
から0までが定番です。
符号なし型ではi >= 0
が常に真になるので、まずは符号付きのint
で書くのが安全です。
#include <stdio.h>
int main(void) {
int N = 5;
for (int i = N - 1; i >= 0; i = i - 1) {
printf("%d ", i);
}
printf("\n");
return 0;
}
4 3 2 1 0
ステップ幅を変える
一定間隔で飛ばしながら回す場合は、更新式でステップ幅を加減します。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 10; i = i + 2) { // 2ずつ増やす
printf("%d ", i);
}
printf("\n");
return 0;
}
0 2 4 6 8
実用例とパターン
配列走査でのfor文の使い方
配列の要素を順に処理するのがfor
の典型例です。
ここでは合計と平均を計算します。
#include <stdio.h>
int main(void) {
int a[] = {3, 1, 4, 1, 5};
int n = (int)(sizeof(a) / sizeof(a[0])); // 要素数
int sum = 0;
for (int i = 0; i < n; i = i + 1) {
sum = sum + a[i];
}
// 平均(整数のまま小数切り捨て)
int avg = sum / n;
printf("合計=%d, 平均=%d\n", sum, avg);
return 0;
}
合計=14, 平均=2
0からn
未満の形にすると、配列外参照を避けやすくなります。
二重ループで表や行列を処理する
二重のfor
で表や行列を扱えます。
次は1〜5の九九表を出力します。
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 5; i = i + 1) { // 行
for (int j = 1; j <= 5; j = j + 1) { // 列
printf("%3d", i * j); // 幅3で整列
}
printf("\n"); // 行の終わりで改行
}
return 0;
}
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
常に{}を付けてバグを防ぐ
本体が1行でも必ず{}
を付けると、インデントだけに頼る勘違いを防げます。
次の2つは見た目が似ていて挙動が違います。
#include <stdio.h>
int main(void) {
// よくあるバグ: 本体は1行だけ。2行目はループ外。
for (int i = 0; i < 3; i = i + 1)
printf("i=%d\n", i);
printf("done(1回だけ)\n");
return 0;
}
i=0
i=1
i=2
done(1回だけ)
#include <stdio.h>
int main(void) {
// 正しい意図: 2行ともループの本体に含める
for (int i = 0; i < 3; i = i + 1) {
printf("i=%d\n", i);
printf("done(毎回)\n");
}
return 0;
}
i=0
done(毎回)
i=1
done(毎回)
i=2
done(毎回)
バグを減らす最も簡単な習慣は、常に波括弧を付けることです。
よくあるミスと対策
オフバイワンを防ぐ(<= と <)
配列のインデックスは0開始で0
〜n-1
です。
i <= n
と書くとa[n]
に触れてしまいます。
// NG例: nが5のとき、iは0..5となり a[5] にアクセスしてしまう
for (int i = 0; i <= n; i = i + 1) {
/* a[i] ... */ // a[5] は配列外
}
// OK例: 0..n-1
for (int i = 0; i < n; i = i + 1) {
/* a[i] ... */
}
<= と < の取り違えは典型的な境界バグです。
配列は未満と覚えましょう。
更新忘れで止まらないようにする
更新式を書かないパターンでは、本体の中で確実に更新する必要があります。
更新を忘れると無限ループになります。
// ヘッダで更新していない。必ず本体で i を進めること。
for (int i = 0; i < 10; /* 更新なし */) {
/* ...処理... */
// i = i + 1; // ← これを忘れるとループが止まらない
}
反復の進み方が複雑になるなら、読みやすさのためにfor
よりwhile
を検討してもよいでしょう(別記事で解説します)。
セミコロンの付け過ぎに注意(for(…);)
ヘッダの直後にセミコロンがあると「空ループ」になり、本体は別扱いになります。
#include <stdio.h>
int main(void) {
int count = 0;
for (int i = 0; i < 3; i = i + 1) ; // ← ここでループ終了(何もしない)
{ // これはただのブロック
count = count + 1;
printf("count=%d\n", count);
}
return 0;
}
count=1
期待した「3回」ではなく1回だけです。
for(…) の直後にセミコロンを書かないように注意します。
ループ変数の型とオーバーフローに注意
符号なし型での逆順ループは危険です。
0から1を引くと大きな数に巻き戻り、止まらなくなります。
// NG: unsigned だと i >= 0 は常に真になり無限ループ
for (unsigned int i = 3; i >= 0; i = i - 1) {
/* ... */
}
安全な書き方の例を2つ示します。
// 1) 符号付きで書く
for (int i = 3; i >= 0; i = i - 1) {
/* ... */
}
// 2) size_t など符号なしのときは条件を「> 0」にして i-1 を使う
for (size_t i = n; i > 0; i = i - 1) {
/* 要素は a[i - 1] を使う */
}
型に応じて条件式と更新式を整えることが大切です。
まとめ
for文は「決まった回数の繰り返し」を最も明快に表現できる構文です。
初期化・条件・更新を1行にまとめ、0からN未満の基本形、1からNまで、逆順、ステップ幅の変更といったお作法を押さえると、配列処理や二重ループが安全かつ読みやすく書けます。
特にオフバイワン、更新忘れ、余計なセミコロン、型による無限ループは初心者だけでなく上級者でも起こしがちです。
常に{}
を付け、初期化→条件→本体→更新の順序と、条件式は0が偽・非0が真という評価規則を意識すれば、堅牢なループが書けるようになります。