
C言語基礎|多次元配列引数の宣言規則
多次元配列って、見た目は「表」や「箱」みたいで直感的なんですが、関数に渡す瞬間にルールが一気に厳しくなるのがC言語の面白いところです。
ポイントはこれです。
- 一番外側(最も高い次元)の要素数だけは省略できる(=可変にしやすい)
- それより内側(n−1次元以下)の要素数は、原則として関数側で分かる形で宣言する必要がある
この「内側サイズが必要」という縛りがあるおかげで、コンパイラは m[i][j] の位置(アドレス)を正しく計算できるようになります。

結論:仮引数の宣言ルール(n次元配列)
n次元の多次元配列を受け取る関数の仮引数は、次のルールで宣言します。
- n 次元(最も外側)の要素数は省略可能
(書いても実質的に使われないので、別の引数で受け取るのが基本) - (n − 1) 次元以下の要素数は、関数側で確定できるように宣言する
(典型的には定数で書く)
1次元〜3次元の典型宣言
| 次元 | 典型的な仮引数宣言 | 何が可変? | 要素型の見え方 |
|---|---|---|---|
| 1次元 | void func1(int v[], int n); | n(要素数) | 要素は int |
| 2次元 | void func2(int v[][3], int n); | n(行数) | 要素は int[3] |
| 3次元 | void func3(int v[][2][3], int n); | n(一番外側の個数) | 要素は int[2][3] |
ここで大事なのは、2次元配列 v[][3] は「int の配列」ではなく、int[3] を要素にもつ配列として扱われる点です。
つまり「1要素=行まるごと」みたいなイメージになります。
どうして内側の要素数が必要なの?
2次元配列 m[i][j] を考えると、i 行目に移動するには
- 1行が何個(何バイト)なのか
が分からないと計算できません。
図:2次元配列は「行サイズ」が道しるべ
m[i][j] の位置
= 先頭 + (i 行ぶんのサイズ) + (j 要素ぶん)
行ぶんのサイズ = 列数 × 1要素のサイズ

だから関数側で m[][列数] の 列数が必要になります。
一方、行数は「何行まで処理するか」を別引数で渡せばよいので、省略しやすいわけです。
サンプルプログラム
- 4週(列数)は固定
- 店舗数(行数)は可変(2店舗でも5店舗でもOK)
という設定にすると、宣言ルールがハマりやすいです。
例:n店舗×4週の売上表を、同じ値で埋めて表示
プロジェクト名:chap6-19-1 ソースファイル名:chap6-19-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
// n店舗×4週の売上表を同じ値で埋めて表示
#include <stdio.h>
#define WEEKS 4
//--- 列数が4の2次元配列 sales の全要素に value を代入 ---//
void fill_sales(int sales[][WEEKS], int stores, int value)
{
for (int i = 0; i < stores; i++)
for (int w = 0; w < WEEKS; w++)
sales[i][w] = value;
}
//--- 列数が4の2次元配列 sales を表示 ---//
void print_sales(const int sales[][WEEKS], int stores)
{
for (int i = 0; i < stores; i++) {
for (int w = 0; w < WEEKS; w++)
printf("%6d", sales[i][w]);
putchar('\n');
}
}
int main(void)
{
int value;
int shopA[2][WEEKS] = {0}; // 2店舗×4週
int shopB[5][WEEKS] = {0}; // 5店舗×4週
printf("全ての週の売上に入れる仮の金額:");
scanf("%d", &value);
fill_sales(shopA, 2, value);
fill_sales(shopB, 5, value);
puts("shopA(2店舗×4週)");
print_sales(shopA, 2);
puts("shopB(5店舗×4週)");
print_sales(shopB, 5);
return 0;
}このプログラムが「宣言規則」をどう使っているか
図:関数に渡しているのは「列数4の表」
main
shopA(2×4) ─┐
├─> fill_sales / print_sales (列数4が前提)
shopB(5×4) ─┘

fill_sales と print_sales の仮引数は sales[][WEEKS] です。
ここで 列数が4であること(WEEKSが4) が関数側で分かっているので、sales[i][w] の計算が成立します。
そして行数(店舗数)は、
- stores という別引数で渡している
ので、2店舗でも5店舗でも同じ関数が使えます。
const を付ける判断(読み取り専用の安心)
今回の print_sales は表示するだけで、配列の中身を書き換えません。
その場合は const を付けて「書き換え禁止」にしておくと安全です。
const を付けるかチェック
| 関数 | 引数 | 中身を書き換える? | 宣言の形 |
|---|---|---|---|
| fill_sales | sales | はい | int sales[][WEEKS] |
| print_sales | sales | いいえ | const int sales[][WEEKS] |
使った命令(書式と役割)
今回出てきた命令まとめ
| 命令 | 書式 | 何をする命令? |
|---|---|---|
| #include | #include <stdio.h> | printf / scanf / puts / putchar などの宣言を取り込む |
| #define | #define WEEKS 4 | マクロ定数を定義する(列数固定に使う) |
| for | for (初期化; 条件; 更新) 文 | 繰り返し(全要素をなめる2重ループ) |
| printf | printf(書式, …); | 書式付き表示 |
| scanf | scanf(書式, アドレス); | キーボード入力を変数へ読み込む |
| puts | puts(文字列); | 文字列を表示して改行 |
| putchar | putchar(文字); | 1文字表示(ここでは改行) |
| const | const 型 | 読み取り専用にする(書き換えミス防止) |
もう一歩だけ:C99以降なら「内側サイズも引数で受け取れる」
ここまでの「内側は定数で」という説明は、まず理解しやすい基本形です。
ただ、C99以降では 可変長配列(VLA) を使って、列数を引数で受け取る書き方もできます。
例(考え方だけ)
- 先に cols を受け取る
- その cols を使って int m[][cols] と宣言する
このやり方は便利ですが、教材の最初は「列数は定数で固定」から入ると混乱しません。
