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 クラスを使用すると、通常の配列と同様の操作に加え、sizefrontbackfill などのメンバー関数を利用できます。ただし、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;
}