Menu

C++ の for ループ:構文、例、よくある間違い

C++ の for ループでコードを繰り返す方法 - 3 つのパートからなるヘッダー、増加・減少のカウント、配列のループ、ネスト、break と continue、そして誰もがはまる off-by-one と符号なし型のバグ。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

なぜ for ループなのか

switch は 1 つの分岐を選んで一度だけ実行します。しかし実際のプログラムでは、何かを繰り返し行う必要があります。すべてのスコアを表示する、数値のリストを合計する、グリッドの 10 行を描画する、といった具合です。for ループは、決まった回数だけコードを繰り返すための C++ の主力で、自分で制御する組み込みのカウンタを備えています。

for ループに必要なものはすべて 1 つのコンパクトなヘッダーに収まっているので、「何回、どのように」という全体像が一目で分かります。

3 つのパートからなるヘッダー

for ループのヘッダーには、セミコロンで区切られた 3 つのパートがあります。初期化子条件更新です。

for (initializer; condition; update) {
    // 本体 - 条件が真である間は実行される
}

これらは特定の順序で実行されます。初期化子は最初に一度だけ実行されます。次に、条件は各反復の前にチェックされます。本体は条件が true の場合のみ実行されます。そして更新は各反復の最後、条件が再びチェックされる直前に実行されます。

ここでは int i = 0 が一度実行されます。次に i < 5 がチェックされ、成り立つ間は本体が表示し、i++ がカウンタを進めます。i5 に達すると条件は false になるので、ループは抜けて done が表示されます。本体はちょうど 5 回実行され、i0 から 4 までの値を取ります。

カウンタをヘッダー内で宣言すること(int i = 0)で、i のスコープはループ内に限定されます。閉じ波かっこの後には存在しません。これはまさに望ましい挙動です。

増加、減少、刻みでのカウント

更新のパートは i++ に限りません。減少させたり、任意の量で刻んだり、配列のインデックスをループしたりできます。

最初のループは i > 0 の間実行され、毎回デクリメントするので 5 4 3 2 1 を表示します。2 つ目は毎回 2 を足し、10 は含めたい値なので <= 10 を使います。条件と更新を合わせましょう。減少のカウントは >>= と、増加のカウントは <<= と組み合わせます。

配列をループする

カウントループの最も一般的な用途は、配列をインデックスでたどることです。カウンタは、読み取る位置も兼ねます。

条件が i <= n ではなく i < n であることに注目してください。5 要素の配列で有効なインデックスは 0 から 4 までで、インデックス 5 は末尾の外です。scores[5] を読み取るのは未定義動作で、ゴミを表示したり、クラッシュしたり、動いているように見えて静かにメモリを壊したりします。i < n のパターンは、ゼロ始まりのあらゆる配列に対する安全なデフォルトです。

インデックスではなく値だけが必要なら、範囲ベース for のほうがすっきりします。実際に位置が必要なときは、古典的なインデックス付きループに手を伸ばしましょう。

break と continue

2 つのキーワードで、ループの途中で流れを変えられます。break はループから即座に抜けます。continue は現在の反復の残りをスキップして更新へジャンプします。

最初のループは 7 を見つけた瞬間に止まり、残りはチェックしません。2 つ目は i が偶数のときに本体の表示をスキップするために continue を使います。更新の i++ は依然として実行されるので、ループは進み続けます。微妙な落とし穴:continue更新へジャンプします。そのため、カウンタをヘッダーではなく本体の中で更新するループで continue に頼ると、その更新をうっかり飛ばして永遠に回り続けることがあります。

ネストしたループ

グリッド、テーブル、ペアを扱うには、for を別の for の中に入れます。内側のループは、外側のループの 1 ステップごとに完全に実行されます。

これは 3x3 の掛け算グリッドを表示します。外側のループが row を固定し、内側のループがその行のすべての col を走査し、それから改行で行を終えます。カウンタには別々の名前を付けましょう(i/i ではなく row/col)。同じ名前を使い回すと外側を覆い隠し、わけの分からないバグを生みます。コストにも注意してください。n 回のループを n 回のループの中にネストすると本体は n * n 回実行され、あっという間に膨らみます。

よくある落とし穴

いくつかの罠が、C++ の for ループのバグの大半を占めます。

  • off-by-one: ゼロ始まりのサイズで i <= n とすると、末尾を越えて 1 要素読み取ってしまいます。i < n を使いましょう。
  • 符号なしのアンダーフロー: 符号なし型で減少のカウントをしても決して負になりません。for (size_t i = n - 1; i >= 0; i--) は永遠にループします。符号なし値では i >= 0 が常に真だからです。i0 のとき i-- は巨大な正の数に巻き戻ります。減少のカウントには符号付きの int を使うか、条件を書き換えましょう。
  • 本体の中でカウンタを変更する: ヘッダーに加えて本体でも i を変えると、ループの回数が予測できなくなります。1 か所に決めましょう。
// BUG: infinite loop - unsigned i is never < 0
for (size_t i = n - 1; i >= 0; i--) {
    process(arr[i]);
}

浮動小数点のカウンタも、もう 1 つの静かな危険です。for (double x = 0.0; x != 1.0; x += 0.1) は、0.1 を正確に格納できないため、1.0 にぴったり到達しないことがあります。整数のカウントでループして値は内部で計算するか、!= の代わりに < を使いましょう。

次へ:while ループ

for ループは、回数が前もって分かっているときに真価を発揮します。しかし、固定の回数ではなく、条件が変わるまで繰り返す必要があることもあります。ファイル終端まで入力を読む、成功するまで再試行する、といった具合です。それは while ループの仕事で、ヘッダーを条件だけに切り詰めます。それが次のページです。

よくある質問

C++ で for ループはどう書きますか?

ヘッダーにセミコロンで区切った 3 つのパート、つまり初期化子・条件・更新を置きます。for (int i = 0; i < 5; i++) { cout << i; }i を 0, 1, 2, 3, 4 と変化させながら本体を実行します。条件が false になった時点でループは停止します。

C++ で for ループと範囲ベース for ループの違いは何ですか?

古典的な for は自分で制御するインデックスカウンタを与えてくれます(for (int i = 0; i < n; i++))。位置が欲しいときや独自の刻みで進めたいときに必要です。範囲ベース forfor (int x : v))はインデックスを隠し、各要素をそのまま渡してくれます。値だけが必要なときはこちらのほうがすっきりします。

なぜ私の C++ の for ループは 1 回多く、または 1 回少なく実行されるのですか?

それが典型的な off-by-one(1 つずれ)のバグです。ゼロ始まりのサイズに対して < の代わりに <= を使うと余分に 1 回反復し、配列の末尾を越えて読み取ります。最後の値を含めたいのに < を使うと 1 回足りなくなります。サイズ n の配列では、安全なパターンは for (int i = 0; i < n; i++) です。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める