C/C++ 配列

配列は、C/C++ において複数の値を保存できる基本的な変数型の一つである。配列を作成するときは、あらかじめ配列の長さ(配列に含まれる要素数)を指定する必要がある。一度作成した配列は、その長さを変更することができない。配列の長さを動的に増やせるようにしたい場合は、メモリの領域を malloc 関数を使い動的に確保し、必要に応じて realloc 関数でメモリ領域を拡大することができる。

配列

配列の宣言

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 次元配列

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 から配列の長さになるまで 1 つずつ増やしながら取り出す。

#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;
}

可変長配列

C/C++ の配列は固定長であり、一度作成した配列の長さを後から変更できない。配列の長さを後から変更できるようにするために、配列を保存するためのメモリ領域を動的に管理する必要がある。例えば、次のコードは整数型の要素を 4 個保存する配列を作成してから、それを 8 個保存できるようにメモリ領域を拡張している例である。なお、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));
        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 関数で解放する必要がある。

array クラス

C++ には配列をより使いやすくした array クラスのデータ型がある。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;
}