10.

C++で配列をfor文で走査する方法|通常forと範囲forの使い方

編集

C++で配列をfor文で走査するには、添字を0から要素数 - 1まで動かす通常のfor文を使うか、要素を直接取り出す範囲for文for (int x : a))を使います。ここではどちらの書き方も、配列の宣言から具体的な走査例、つまずきやすい落とし穴まで順に整理します。

この記事の要点
  • 配列はint a[5];のように、型・名前・要素数を指定して宣言します。
  • 通常のfor文では、添字i0から要素数 - 1まで動かして各要素にアクセスします。
  • 範囲for文(for (int x : a))は添字を意識せず全要素を順に取り出せます。要素を書き換える場合は参照(int& x)を使います。
  • 添字は0始まりで、範囲外アクセスは未定義動作になるため注意が必要です。
  • 要素数の求め方は配列の要素数、二次元以上の配列の走査は多次元配列とfor文を参照してください。

配列の宣言

配列は、同じ型の値を連続して並べて扱うためのものです。宣言では名前要素数を指定します。次の例はint型の要素を5個持つ配列aを宣言します。

int a[5];  // int型の要素を5個持つ配列

 

各要素にはa[0]a[1]、…のように添字(インデックス)でアクセスします。添字は0から始まるため、要素数が5個なら有効な添字は0から4までです。宣言と同時に値を並べて初期化することもできます。

int a[5] = {10, 20, 30, 40, 50};

 

通常のfor文での走査

配列のすべての要素を順に処理する最も基本的な方法が、添字を動かす通常のfor文です。添字i0から始め、i < 要素数の条件で繰り返し、各回でa[i]にアクセスします。条件をi <= 要素数ではなくi < 要素数とする点が重要です。

#include <iostream>

int main() {

    int a[5] = {10, 20, 30, 40, 50};

    for (int i = 0; i < 5; i++) {
        std::cout << a[i] << "\n";
    }
    return 0;
}

 

実行すると、各要素が1行ずつ出力されます。

10
20
30
40
50

 

この書き方では添字iそのものを使えるため、「何番目の要素か」を処理に利用したい場合や、要素を書き換えたい場合に向いています。

範囲for文での走査

要素を先頭から順に取り出すだけでよい場合は、範囲for文(range-based for)が簡潔です。添字や要素数を書く必要がなく、配列の全要素を1つずつ変数に取り出して処理できます。

#include <iostream>

int main() {

    int a[5] = {10, 20, 30, 40, 50};

    for (int x : a) {
        std::cout << x << "\n";
    }
    return 0;
}

 

出力は通常のfor文の例と同じです。for (int x : a)と書くと、xには各要素のコピーが入ります。そのためxを書き換えても、元の配列aの中身は変わりません。

配列の要素そのものを書き換えたい場合は、変数を参照として受け取ります。型に&を付けてfor (int& x : a)と書きます。

int a[5] = {10, 20, 30, 40, 50};

for (int& x : a) {
    x *= 2;  // 各要素を2倍に書き換える
}

// a は {20, 40, 60, 80, 100} になる

 

要素を書き換えずに読み取るだけなら、コピーを避けつつ変更を禁止できるconst int& xもよく使われます。intのような小さな型では差はわずかですが、サイズの大きな型を要素に持つ配列では効果があります。

通常のfor文と範囲for文の使い分け
  • 通常のfor文:添字(何番目か)を使いたい、一部の要素だけ処理したい、逆順に走査したい場合に向く。
  • 範囲for文:全要素を先頭から順に処理するだけの場合に簡潔。書き換えるならint& x、読むだけならint xまたはconst int& x

配列サイズの扱い

通常のfor文では繰り返し条件に要素数が必要です。上の例では5という数値を直接書きましたが、要素数を変えたときに条件側の修正を忘れると不具合の原因になります。要素数は値を直接書くのではなく、求めて使うほうが安全です。要素数の具体的な求め方は配列の要素数で詳しく解説しています。

なお、範囲for文は内部で配列の要素数を自動的に扱うため、要素数を書く必要がありません。これも範囲for文の利点の1つです。

多次元配列の場合

行と列を持つ二次元配列のように、配列を入れ子にした多次元配列では、for文も入れ子にして走査します。書き方は一次元の場合と考え方は同じですが、添字が増える分の注意点があります。多次元配列の宣言と走査については多次元配列とfor文を参照してください。

初期化と走査の例

実際の処理では、配列を初期化したうえで走査し、合計や最大値などを求めることがよくあります。次の例は、配列の全要素の合計を通常のfor文で求めます。

#include <iostream>

int main() {

    int a[5] = {10, 20, 30, 40, 50};

    int sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += a[i];
    }

    std::cout << "合計: " << sum << "\n";
    return 0;
}

 

同じ合計は、読み取るだけなので範囲for文でも書けます。

int a[5] = {10, 20, 30, 40, 50};

int sum = 0;
for (int x : a) {
    sum += x;
}
// sum は 150 になる

 

落とし穴

注意すべきポイント
  • 範囲外アクセス:要素数5の配列でa[5]にアクセスすると、有効範囲外(添字は04)になります。C++では範囲外アクセスは未定義動作で、エラーにならず誤った値が読めたり書けたりすることがあるため特に危険です。条件はi <= 5ではなくi < 5とします。
  • 添字の0始まり:先頭要素はa[1]ではなくa[0]です。i = 1から始めると先頭を読み飛ばします。
  • ポインタへの減衰:配列を関数の引数に渡すと、配列は先頭要素を指すポインタに変換(減衰)され、要素数の情報が失われます。受け取った側で範囲for文を使ったり要素数を求めたりはできないため、要素数は別の引数で渡すなどの対応が必要です。
  • 範囲for文でのコピーfor (int x : a)ではxはコピーです。要素を書き換えたつもりが反映されない場合は、参照int& xになっているか確認します。

よくある質問

Q. 通常のfor文と範囲for文はどちらを使うべきですか?

A. 全要素を先頭から順に処理するだけなら範囲for文が簡潔で、要素数の書き間違いや範囲外アクセスを避けやすくなります。一方、何番目の要素かを使いたい、逆順に走査したい、一部の要素だけ処理したいといった場合は、添字を扱える通常のfor文が適しています。

Q. 範囲for文で要素を書き換えても元の配列が変わりません。

A. for (int x : a)xは各要素のコピーのため、xを変更しても元の配列には反映されません。元の要素を書き換えたい場合は、参照で受け取るfor (int& x : a)と書きます。

Q. 配列の要素数を直接書かずに走査したいです。

A. 範囲for文を使えば要素数を書かずに全要素を走査できます。通常のfor文で要素数が必要な場合は、数値を直接書くのではなく要素数を求めて使うと、要素数を変えたときの修正漏れを防げます。求め方は配列の要素数を参照してください。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. コメントアウト
  2. 文字列の結合/連結
  3. 変数の宣言
  4. 定数の宣言
  5. if文
  6. if文の論理演算子
  7. for文
  8. データ型(文字列以外)
  9. データ型(文字列)
  10. 配列とfor文
  11. 配列の要素数
  12. 多次元配列とfor文
  13. 多次元配列の要素数
  14. 関数の定義と呼び出し

最近更新/作成されたページ