2.12.2. C/C++ 配列#
配列は、C/C++ において複数の値を保存できる基本的な変数型の一つです。配列を作成するときは、あらかじめ配列の長さ(配列に含まれる要素数)を指定する必要があります。一度作成した配列は、その長さを変更できません。配列の長さを動的に増やしたい場合は、メモリ領域を malloc 関数で動的に確保し、必要に応じて realloc 関数でメモリ領域を拡張します。
2.12.2.1. 配列#
C/C++ で配列を作成する場合は、あらかじめサイズを指定する必要があります。例えば、10 個の要素からなる配列を宣言する場合は int arr[10] のように記述します。int arr[10] と宣言すると、arr[0] から arr[9] までの 10 個の要素を保存するための領域が、メモリ上のどこかに確保されます。しかし、arr[10] や arr[11] の領域は確保されていないため、これらの要素にアクセスすると、コンパイル時エラーになる場合や、実行時に Segmentation fault が発生する場合があります。実行環境によっては偶然動作する場合もありますが、未定義動作であり非常に危険です。
#include <cstdio>
int main() {
int arr[10];
arr[0] = 1;
arr[1] = 1;
arr[2] = 3;
printf("%d\n", arr[0]); // 1
printf("%d\n", arr[1]); // 1
printf("%d\n", arr[2]); // 2
printf("%d\n", arr[3]); // 0
return 0;
}
配列の各要素の値があらかじめ分かっている場合は、次のように宣言できます。
#include <cstdio>
int main() {
int arr1[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
int arr2[10] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
int arr3[10] = {1, 1, 2, 3, 5, 8};
printf("%d", arr3[9]); // 0
return 0;
}
配列から要素を取り出すときは、0 から始まる整数値を指定します。
#include <cstdio>
int main() {
int arr[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
printf("%d\n", arr[0]); // 1
printf("%d\n", arr[1]); // 1
printf("%d\n", arr[2]); // 2
printf("%d\n", arr[3]); // 0
return 0;
}
すべての要素を順に取り出す場合は、位置番号を 0 から配列の要素数になるまで 1 ずつ増やします。配列の長さは、配列全体のメモリサイズを配列の 0 番目の要素のメモリサイズで割ることで求められます。
#include <cstdio>
int main() {
int arr[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
int arr_length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < arr_length; i++) {
printf("%d\n", arr[i]);
}
return 0;
}
2 次元配列を宣言する場合は、1 次元目と 2 次元目それぞれの要素数を指定する必要があります。
#include <cstdio>
int main() {
int arr[4][3];
arr[0][0] = 11;
arr[0][1] = 12;
arr[0][2] = 13;
arr[1][0] = 21;
arr[1][1] = 22;
arr[1][2] = 23;
return 0;
}
2 次元配列の各要素が既知である場合は、次のように宣言できます。
#include <cstdio>
int main() {
int arr[4][3] = {{11, 12, 13},
{21, 22, 23},
{31, 32, 33},
{41, 42, 43}};
return 0;
}
配列中の各要素を取り出すときは、1 次元配列と同様に添字を 0 から順に増やします。
#include <cstdio>
int main() {
int arr[4][3] = {{11, 12, 13},
{21, 22, 23},
{31, 32, 33},
{41, 42, 43}};
printf("%lu\n", sizeof(arr)); // 48
printf("%lu\n", sizeof(arr[0])); // 12
printf("%lu\n", sizeof(arr[0][0])); // 4
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
for (int j = 0; j < sizeof(arr[0]) / sizeof(arr[0][0]); j++) {
printf("%d\n", arr[i][j]);
}
}
return 0;
}
2.12.2.2. 可変長配列#
C/C++ の配列は固定長であり、一度作成すると長さを変更できません。配列の長さを後から変更したい場合は、配列用のメモリ領域を動的に管理します。次のコードでは、整数型の要素を 4 個保存する配列を作成し、その後 16 個保存できるように拡張しています。malloc および realloc で確保した領域には不定値が入るため、memset 関数ですべての要素を 0 に初期化しています。
#include <cstdlib>
#include <cstdio>
#include <cstring>
int main() {
int n = 4;
int *arr;
arr = (int *) malloc(sizeof(int) * n);
if (arr == NULL) {
printf("Memory cannot be allocated.");
} else {
memset(arr, 0, sizeof(int) * n);
arr[0] = 1;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
}
int m = 16;
arr = (int *) realloc(arr, sizeof(int) * m);
if (arr == NULL) {
printf("Memory cannot be enlarged.");
} else {
memset(&(arr[n]), 0, sizeof(int) * (m - n));
arr[4] = 5;
arr[5] = 8;
}
for (int i = 0; i < m; i++) {
printf("%p %d\n", &(arr[i]), arr[i]);
}
free(arr);
return 0;
}
一度確保したメモリ領域は、使用しなくなった時点で free 関数により解放する必要があります。
2.12.2.3. array クラス#
C++ には、配列をより安全かつ便利に扱える array クラス(英語:array class)があります。array クラスを使用すると、通常の配列と同様の操作に加え、size、front、back、fill などのメンバー関数を利用できます。ただし、array クラスも固定長であり、一度作成すると後から長さを変更できません。可変長配列を扱いたい場合は、C++ の Vector クラスや List クラスが便利です。
#include <iostream>
#include <array>
int main() {
std::array<int, 10> arr{1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
if (!arr.empty()) {
std::cout << "array size: " << arr.size() << std::endl; // 10
}
std::cout << "-------------------------" << std::endl;
for (int i = 0; i < arr.size(); i++) {
std::cout << arr[i] << std::endl;
}
std::cout << "-------------------------" << std::endl;
std::cout << "first element: " << arr.front() << std::endl; // 1
std::cout << "last element: " << arr.back() << std::endl; // 55
return 0;
}
fill 関数を使用すると、配列中のすべての要素を同じ値で埋められます。次のコードは、長さ 10 の array を生成し、各要素を 256 で埋める例です。
#include <iostream>
#include <array>
int main() {
std::array<int, 10> arr;
arr.fill(256);
return 0;
}