配列(1次元・多次元)
| 対応: | C89(1989) |
|---|
同じ型のデータを連続したメモリ領域に並べたデータ構造です。インデックス(添字)で各要素にアクセスします。1次元配列のほか、表形式のデータを扱う多次元配列も使えます。
構文
// 1次元配列の宣言です。
型 配列名[要素数];
// 宣言と同時に初期化します。
型 配列名[要素数] = {値1, 値2, 値3};
// 要素数を省略して初期化子から自動決定します。
型 配列名[] = {値1, 値2, 値3};
// 要素へのアクセスです(インデックスは0始まり)。
配列名[インデックス];
// 2次元配列の宣言と初期化です。
型 配列名[行数][列数] = {{値, 値}, {値, 値}};
配列の特徴
| 特徴 | 概要 |
|---|---|
| インデックスは0始まり | 最初の要素のインデックスは0、最後の要素は『要素数 - 1』です。 |
| メモリの連続性 | 配列の要素はメモリ上に連続して配置されます。ポインタ演算でアクセスできます。 |
| 固定サイズ | 宣言時にサイズが決まり、実行中に変更できません。可変サイズが必要な場合は動的メモリを使います。 |
| 配列名はアドレス | 配列名は先頭要素のアドレスを表します。関数に渡すとポインタに変換されます。 |
| 文字列は char 配列 | 文字列は『char』の配列で表現し、末尾にヌル文字('\0')が置かれます。 |
サンプルコード
1次元配列の宣言・初期化・ループ処理と、char 配列による文字列表現を示します。
sample_array.c
#include <stdio.h>
int main(void) {
int scores[5] = {80, 75, 90, 65, 88};
printf("最初の要素: %d\n", scores[0]);
printf("最後の要素: %d\n", scores[4]);
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += scores[i];
}
printf("合計: %d\n", sum);
printf("平均: %.1f\n", (double)sum / 5);
int count = sizeof(scores) / sizeof(scores[0]);
printf("要素数: %d\n", count);
char name[] = "八神庵";
printf("名前: %s\n", name);
return 0;
}
コンパイルして実行すると次のようになります。
gcc sample_array.c -o sample_array ./sample_array 最初の要素: 80 最後の要素: 88 合計: 398 平均: 79.6 要素数: 5 名前: 八神庵
2次元配列
2次元配列は表形式のデータを扱うのに適しています。各行・列をネストした for ループで処理します。
array_2d.c
#include <stdio.h>
int main(void) {
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
int sum = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
sum += matrix[i][j];
}
}
printf("合計: %d\n", sum);
return 0;
}
コンパイルして実行すると次のようになります。
gcc array_2d.c -o array_2d ./array_2d 1 2 3 4 5 6 7 8 9 合計: 45
配列の初期化パターン
配列の初期化には複数の方法があります。部分初期化では残りの要素が 0 になります。
array_init.c
#include <stdio.h>
int main(void) {
int a[5] = {10, 20, 30};
printf("部分初期化: ");
for (int i = 0; i < 5; i++) {
printf("%d ", a[i]);
}
printf("\n");
int b[5] = {0};
printf("ゼロ初期化: ");
for (int i = 0; i < 5; i++) {
printf("%d ", b[i]);
}
printf("\n");
int c[] = {100, 200, 300, 400};
int n = sizeof(c) / sizeof(c[0]);
printf("要素数自動決定: %d 個\n", n);
return 0;
}
コンパイルして実行すると次のようになります。
gcc array_init.c -o array_init ./array_init 部分初期化: 10 20 30 0 0 ゼロ初期化: 0 0 0 0 0 要素数自動決定: 4 個
よくあるミス
よくあるミス: 範囲外アクセス
配列の範囲外にアクセスすると未定義動作が発生します。C言語はバウンダリチェックを行いません。
array_oob_ng.c
#include <stdio.h>
int main(void) {
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[5]); /* 要素数 5 なのでインデックス 5 は範囲外 */
return 0;
}
修正後は次の通りです。
gcc array_oob_ng.c -o array_oob_ng ./array_oob_ng -1735289080
配列外のメモリを読むため、不定値が返ります。インデックスは常に 0 以上 要素数未満の範囲で使います。
array_oob_ok.c
#include <stdio.h>
int main(void) {
int arr[5] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < n; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
修正後は次の通りです。
gcc array_oob_ok.c -o array_oob_ok ./array_oob_ok 1 2 3 4 5
概要
C言語の配列はバウンダリチェック(範囲検査)を行いません。配列の範囲外にアクセスすると未定義動作が発生し、プログラムがクラッシュしたり、別のメモリ領域のデータを破壊したりします。インデックスが常に有効な範囲内に収まるよう注意してください。
配列とポインタは密接な関係にあります。『scores[i]』は『*(scores + i)』と等価です。配列を関数に渡す際の扱いについては『ポインタと配列』を参照してください。
実行時にサイズを決めたい場合は動的メモリ確保(『malloc()』)を使用してください。C99以降では可変長配列(VLA)も使えますが、スタックオーバーフローのリスクがあるため大きなサイズには使わないでください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。