for文は定型的な繰り返し処理を1行にまとめて書けるC言語の基本構文です。
本記事では初期化・条件・更新の順番から基本パターン、注意点、while文との使い分けまで、入門者がつまずきやすい点を段階的に解説します。
実行例を交え、真似しやすい形で理解を固めていきます。
for文とは?
for文の役割と使いどころ
for文は、回数が明確な繰り返しに最適な構文です。
配列の全要素を処理したり、同じ処理を一定回数だけ繰り返したいときに読みやすく、間違いを防ぎやすい書き方ができます。
初期化、継続条件、カウンタの更新がヘッダにまとまるため、ループの意図が一目でわかるのが特徴です。
初期化・条件・更新の順番
for文は次の順番で実行されます。
- 初期化を最初に1回だけ評価します。
- 条件を評価し、真(非0)なら本体を実行、偽(0)ならループを終了します。
- 本体を実行し終えたら更新を評価します。
- ふたたび条件の評価に戻ります。
したがって、更新は各反復の末尾で実行され、条件は反復の先頭で毎回評価されます。
for文の構文(書き方)
C言語の一般形は次の通りです。
/* 一般形(概念)
for (初期化; 条件; 更新) {
// 繰り返したい処理(0回以上繰り返される)
}
*/
各部は任意で、省略することもできます。
例えば、初期化や更新を空にすれば、ヘッダ以外に書いた処理が使われます。
条件を省略すると常に真となり無限ループになります。
初期化の書き方と変数スコープ
C99以降では、forの初期化部でループ変数を宣言できます。
宣言した変数はループのスコープ(波括弧の内側)でのみ有効です。
// C99以降: ループ内だけでiが見える
for (int i = 0; i < 5; i++) {
/* iはここで使える */
}
/* ここではiは見えない(スコープ外) */
ループの外でも変数を使いたい場合や、C90準拠の古いコンパイラでは、先に宣言してから使います。
int i; // 先に宣言
for (i = 0; i < 5; i++) {
/* ... */
}
printf("%d\n", i); // ここでもiが使える
なお、複数の変数を初期化するにはカンマ演算子が使えます。
for (int i = 0, j = 10; i < j; i++, j--) {
// iは増加、jは減少
}
条件式の作り方
条件式は「続けるかどうか」を決める部分で、典型的には比較演算子(<
, <=
, >
, >=
, ==
, !=
)を使います。
配列の要素数がnなら、インデックスは0からn-1が有効範囲なので i < n
と書くのが安全で読みやすい定石です。
代入演算子=
と等値比較==
の取り違えに注意してください。
また、Cでは0が偽、それ以外が真です。
例えばポインタp
がNULLでない間繰り返すなら、for (; p; p = p->next) { ... }
のように簡潔に書けます。
更新の書き方(インクリメント/デクリメント)
更新では、ループ変数を進めます。
最もよく使うのは次の書き方です。
- 1ずつ増やす:
i++
または++i
- 1ずつ減らす:
i--
または--i
- 任意の幅で増やす/減らす:
i += 2
,i -= 3
など
for文の更新部では i++
と ++i
の挙動差は実質ありません。
式の値を使わず副作用だけが必要だからです。
可読性を優先して一貫したスタイルを選びましょう。
for文の使い方(基本パターンと例)
0からn-1までのカウンタループ
最も頻出するパターンです。
配列や文字列の走査にも直結します。
#include <stdio.h>
int main(void) {
int n = 5;
// 0,1,2,3,4 と5回繰り返す
for (int i = 0; i < n; i++) {
printf("%d ", i); // iは0からn-1
}
printf("\n");
return 0;
}
出力例:
0 1 2 3 4
降順ループ(逆順)
大きい値から小さい値へ進めたいときはデクリメントを使います。
符号なし型では書き方に注意が必要です。
#include <stdio.h>
#include <stddef.h>
int main(void) {
int n = 5;
// 符号付きintを使った降順
printf("signed: ");
for (int i = n - 1; i >= 0; i--) {
printf("%d ", i);
}
printf("\n");
// 符号なし(size_t)での安全な降順
// i >= 0 は常に真になるので使わない
size_t m = 5;
printf("size_t: ");
for (size_t i = m; i-- > 0; ) { // iをデクリメントした結果が0より大きい間
printf("%zu ", i);
}
printf("\n");
return 0;
}
出力例:
signed: 4 3 2 1 0
size_t: 4 3 2 1 0
ステップ幅の指定(i+=2など)
偶数だけ、3刻みなど任意のステップで進められます。
#include <stdio.h>
int main(void) {
// 偶数だけを列挙
printf("step 2: ");
for (int i = 0; i < 10; i += 2) {
printf("%d ", i);
}
printf("\n");
// 1から10までを3刻み
printf("step 3: ");
for (int i = 1; i <= 10; i += 3) {
printf("%d ", i);
}
printf("\n");
return 0;
}
出力例:
step 2: 0 2 4 6 8
step 3: 1 4 7 10
配列をforで走査
配列の長さは sizeof(a) / sizeof(a[0])
で求めるのが定番です。
#include <stdio.h>
int main(void) {
int a[] = {10, 20, 30, 40};
size_t len = sizeof(a) / sizeof(a[0]);
for (size_t i = 0; i < len; i++) {
printf("a[%zu] = %d\n", i, a[i]);
}
return 0;
}
出力例:
a[0] = 10
a[1] = 20
a[2] = 30
a[3] = 40
ネストしたfor文
2重ループで表や行列、九九などを作れます。
#include <stdio.h>
int main(void) {
// 3x3の簡単な掛け算表
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
printf("%2d ", i * j); // 幅2で整形
}
printf("\n");
}
return 0;
}
出力例:
1 2 3
2 4 6
3 6 9
制御構文と注意点
break/continueの使い方
break
はループ自体を脱出し、continue
はその反復の残りをスキップして次の反復に進みます。
#include <stdio.h>
int main(void) {
// continue: 奇数を飛ばして偶数だけ表示
printf("continue: ");
for (int i = 1; i <= 10; i++) {
if (i % 2 != 0) {
continue; // 奇数はスキップ
}
printf("%d ", i);
}
printf("\n");
// break: 7で打ち切る
printf("break: ");
for (int i = 1; i <= 10; i++) {
if (i == 7) {
break; // 7になったらループ終了
}
printf("%d ", i);
}
printf("\n");
return 0;
}
出力例:
continue: 2 4 6 8 10
break: 1 2 3 4 5 6
無限ループ(for(;;))の書き方
for(;;)
は条件を省略した無限ループです。
明示的にbreak
するまで続きます。
#include <stdio.h>
int main(void) {
int count = 0;
// 実際には3回でbreakして終了
for (;;) { // 無限ループの典型形
count++;
printf("tick %d\n", count);
if (count == 3) {
break; // 条件が満たされたら抜ける
}
}
return 0;
}
出力例:
tick 1
tick 2
tick 3
オフバイワンエラーを防ぐ条件
配列の添字は0から始まるため、典型的な安全条件は < 長さ
です。
<=
と書くと1回余計に回って範囲外アクセスになる危険があります。
/* 誤り例: a[3] にアクセスしてしまう(未定義動作の危険)
int a[3] = {1,2,3};
for (int i = 0; i <= 3; i++) { // <= 3 は1回多い
printf("%d\n", a[i]); // i == 3 で範囲外
}
*/
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
// 正しい: i は 0,1,2 の3回
for (int i = 0; i < 3; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
出力例:
1 2 3
よく使う範囲と条件の対応は次の通りです。
取りたい範囲 | 初期化 | 条件 | 更新 |
---|---|---|---|
0..n-1 | i = 0 | i < n | i++ |
1..n | i = 1 | i <= n | i++ |
m..n(両端含む) | i = m | i <= n | i++ |
m..n(上端を含まない) | i = m | i < n | i++ |
降順 n-1..0 | i = n-1 | i >= 0 | i– |
降順(size_t) | i = n | i– > 0 | (更新部に i–) |
for(…); の空文に注意
forの直後にセミコロンがあると、ループ本体が空文になってしまいます。
ブロックが別物として1回だけ実行されるため、意図と違う結果になります。
#include <stdio.h>
int main(void) {
int count = 0;
for (int i = 0; i < 3; i++); // ← ここでループが終わってしまう(空文)
{
count++; // これは1回だけ実行される
}
printf("count = %d\n", count);
// 正しい書き方
count = 0;
for (int i = 0; i < 3; i++) {
count++;
}
printf("count = %d\n", count);
return 0;
}
出力例:
count = 1
count = 3
for文とwhile文の違いと使い分け
構文の違いと読みやすさ
for
は「初期化・条件・更新」を1行にまとめ、反復の全体像を明確にします。
while
は「条件だけ」を書くため、初期化や更新の場所が離れる分、柔軟ですが読み手が追跡しにくくなることがあります。
両者はしばしば等価に書けます。
#include <stdio.h>
int main(void) {
// for版
printf("for: ");
for (int i = 0; i < 5; i++) {
printf("%d ", i);
}
printf("\n");
// while版(上のfor文と等価)
printf("while: ");
int j = 0; // 初期化
while (j < 5) { // 条件
printf("%d ", j);
j++; // 更新
}
printf("\n");
return 0;
}
出力例:
for: 0 1 2 3 4
while: 0 1 2 3 4
for文を選ぶ場面/whileを選ぶ場面
回数がはっきりしている、または「開始値から終了値まで規則的に進む」処理ならfor
が自然です。
配列走査、カウンタ制御、表の生成などが該当します。
初期化・条件・更新がまとまり、ヒューマンエラー(更新忘れ、条件の抜け)を減らせます。
一方で、入力が枯渇するまで読み続ける、条件が複雑で更新の位置もケースによって変わる、リスナーがイベントを待ち続けるといった「回数が未確定なループ」はwhile
の方が意図に合います。
また、for(;;)
で無限ループを書くことも可能ですが、無限ループを扱う設計上の文脈ではwhile(1)
と書かれることもあります。
プロジェクトのスタイルガイドに従い、意図が最も伝わる形を選ぶのが良いでしょう。
観点 | for | while |
---|---|---|
初期化・条件・更新 | 1行に集約 | 分散しがち |
読みやすさ | 反復の全体像が明確 | 自由度が高いが追跡が必要 |
典型用途 | 配列走査、固定回数、範囲ループ | 入力待ち、条件が変動、未確定回数 |
無限ループ | for(;;) | while(1) |
まとめ
for文は、反復の初期化・条件・更新をひとまとめにして意図を明確にできる、C言語の基本かつ強力な構文です。
0からn-1の標準形、降順、ステップ幅、配列走査、ネストなどの基本パターンを押さえ、break
/continue
の挙動やfor(;;)
の無限ループの扱いも理解しておくと、安全で読みやすいコードを書けます。
特にオフバイワンエラーはi < n
を徹底することで多くを防げます。
while文との違いも踏まえ、回数が明確なときはfor、未確定なときはwhileと使い分ける習慣を身につけてください。