トラック8・🗃️配列

多次元配列

読む目安 約12分・ゴール:2次元配列を宣言・初期化し、二重ループで表として扱える

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

配列の要素を、さらに配列にできます。こうしてできるのが多次元配列で、いちばん出番が多いのは2次元です。表・マス目・座席表・画像——縦横に並ぶものは、たいていこれで表せます。

int m[2][3];     // 2行3列の表(要素は 2 × 3 = 6個)

読み方は「int が3個入った配列が、2個並んだもの」。先の [2] が行数、後ろの [3] が列数です。アクセスは m[行][列] の順で、どちらの添字も0始まりです。

どんなときに使うか

「行と列がある」ものはすべて2次元配列の出番です。カレンダー、座席表、迷路のマス目、オセロの盤面、画像の画素、行列の計算。紙に書いたときに表になるデータなら、素直に [行][列] で持つのがいちばん読みやすくなります。

#include <stdio.h>

int main(void)
{
    int m[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    printf("%d\n", m[0][0]);   // 1
    printf("%d\n", m[1][2]);   // 6

    for (int r = 0; r < 2; r++) {
        for (int c = 0; c < 3; c++) {
            printf("%3d", m[r][c]);
        }
        printf("\n");
    }
    return 0;
}

初期化は内側の { } で行ごとに区切ると、見た目がそのまま表になって読みやすくなります。ここでも足りないぶんは0で埋まります。

二重ループとの相性

外側のループで行、内側のループで列を回すのが定石です。ループ変数を i j にすると取り違えやすいので、r(row)と c(column)のように意味のある名前にすると事故が減ります。

#include <stdio.h>

int main(void)
{
    int table[9][9];

    for (int r = 0; r < 9; r++) {
        for (int c = 0; c < 9; c++) {
            table[r][c] = (r + 1) * (c + 1);   // 九九表
        }
    }
    printf("%d\n", table[6][7]);   // 7 × 8 = 56
    return 0;
}

本当は一列に並んでいる

int m[2][3] はメモリ上で 6個の int が一列に並んでいます。並び順は m[0][0], m[0][1], m[0][2], m[1][0], … と、行を先に埋めていく順(行優先)です。表に見えているのは人間の都合で、実体は1本の帯だと知っておくと、第2部でポインタを学ぶときにすんなり繋がります。

この性質を利用して、int m[3][4] の代わりに int m[12] を用意し、m[r * 4 + c] と自分で計算する書き方もできます。メモリ上の並びはまったく同じです。ただし添字の計算を1つ間違えれば即座に境界外アクセスになるので、2次元で書けるところは素直に2次元で書きましょう。

罠・注意

まとめ: 2次元配列は 型 名前[行][列]、アクセスは 名前[行][列]。二重ループで回し、メモリ上は行優先で一列に並んでいます。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

次のプログラムの出力はどれですか。

#include <stdio.h>

int main(void)
{
    int m[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    printf("%d %d\n", m[1][0], m[0][2]);
    return 0;
}
  1. 2 4
  2. 4 3
  3. 3 4
  4. 4 6
答えを見る

正解:B(4 3)

m[行][列] の順で、どちらも0始まりです。m[1][0] は2行目の1列目で 4、m[0][2] は1行目の3列目で 3 になります。行と列を取り違えないよう、変数名を r・c にすると混乱が減ります。

確認問題 2

int m[3][4]; について、正しい説明はどれですか。

  1. 要素は全部で7個で、m[3][4] にアクセスできる
  2. 要素は全部で12個で、メモリ上は m[0][0], m[0][1], … と行ごとに一列に並ぶ
  3. 要素は全部で12個で、m[i, j] という書き方でもアクセスできる
  4. 1次元配列より必ず遅くなる
答えを見る

正解:B(要素は全部で12個で、メモリ上は m[0][0], m[0][1], … と行ごとに一列に並ぶ)

要素数は 3 × 4 = 12 で、メモリ上は行優先で一列に並びます。行の添字は 0〜2、列は 0〜3 なので m[3][4] は境界外です。m[i, j] はカンマ演算子と解釈されるため誤りで、必ず m[i][j] と書きます。

「配列」の目次

  1. 配列とは——箱の行列
  2. 添字と0始まりの理由
  3. 配列とループの黄金コンビ
  4. 境界外アクセスという重大事故
  5. 配列の初期化いろいろ
  6. 多次元配列
  7. 配列を関数に渡す——入口編
  8. 集計・最大値・検索の定石

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版