C言語

【C言語】配列の要素数を実行時に変更する方法【動的配列】

この記事では、C言語の動的配列について解説します。

動的配列とは、動的に確保したメモリを使った配列のことを言います。メモリの確保数を変更することで、配列の要素数を動的に変更することができます。

ちなみに、要素数が固定されている配列のことを「静的配列」と呼びます。

それでは、C言語の動的配列の使い方を見ていきましょう!

メモリの動的な確保

メモリを動的に確保するには、以下の関数を使います。

malloc メモリを動的に確保する。
calloc メモリを動的に確保し、ゼロクリアする。
alloca メモリをスタックから取る。スコープから外れる時に自動的に解放される。

これらの関数はstdlib.hをインクルードして使います。

#include <stdlib.h> 

malloc関数(エムアロック、マロック)

malloc関数は、引数で指定されたバイト分のメモリを確保します。戻り値は確保したメモリの先頭のアドレスを返すのでポインタで受け取ります。

型 *ポインタ変数 = (型*)malloc(sizeof(型) * 要素数);
  • メモリが確保できなかった場合はNULLが返る
  • 動的に確保・解放したメモリのことをヒープと呼びます
  • ヒープに確保された配列を動的配列と呼びます

calloc関数(シーアロック、カロック)

calloc関数は、引数に要素数と1つの要素のサイズを指定します。

型 *ポインタ変数 = (型*)calloc(要素数, sizeof(型));

alloca関数(アロカ)

alloca関数は、malloc関数と同様に使用します。
※ 標準関数ではない

型 *ポインタ変数 = (型*)alloca(sizeof(型) * 要素数);

メモリの再確保

確保したメモリが足りなくなってしまった場合は、realloc関数を使ってメモリを再確保することができます。

メモリを拡張するのではなく、新しくメモリを確保し、そこにデータをコピーするのでサイズが大きいほど再確保にコストがかかります。

使い方

引数に元のメモリのポインタと確保する要素数を指定します。

ポインタ = (型*)realloc(ポインタ, sizeof(型) * 要素数);

直接ポインタに再確保したメモリを渡すと、realloc関数が失敗した場合に元のポインタにアクセスできなくなってしまいます。なので、一時的に他のポインタに代入し、正常にメモリが再確保できたか確認する必要があります。

サンプル

メモリを再確保するサンプル見てみます。

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    // メモリの確保
    int *nums = (int*)malloc(sizeof(int) * 3);
    // メモリを確保できなかった場合の処理
    if(nums == NULL) {
        printf("メモリを確保できませんでした!\n");
        exit(EXIT_FAILURE);
    }

    // メモリの再確保
    int *tmp = (int*)realloc(nums, sizeof(int) * 5);
    // メモリを再確保できなかった場合の処理
    if(tmp == NULL){
        free(nums);
        printf("メモリを再確保できませんでした!\n");
        exit(EXIT_FAILURE);
    }

    // メモリが正常に再確保できたのでnumsに代入
    nums = tmp;
}

メモリの解放

確保したメモリはプログラム終了時まで保持されます。なので、必要なくなったメモリはfree関数で明示的に解放してやりましょう。

使い方

引数に確保したメモリを保持するポインタを渡します。

free(ポインタ);
  • メモリを解放せずに確保し続けてしまうことをメモリリークと呼ぶ
  • alloca関数は、free関数で解放してはいけません!

配列として使ってみる

試しにmalloc関数で動的にメモリを確保し、配列として使ってみましょう!

#include <stdio.h>
#include <stdlib.h>

int main(void) {

    int size;
    printf("要素数: ");
    scanf("%d", &size);

    // メモリの確保
    int *nums = (int*)malloc(sizeof(int) * size);
    // メモリを確保できなかった場合の処理
    if(nums == NULL) {
        printf("メモリを確保できませんでした!\n");
        exit(EXIT_FAILURE);
    }

    // 初期化
    for(int i = 0; i < size; i++){
        nums[i] = i+1;
    }

    int resize;
    printf("再確保する要素数: ");
    scanf("%d", &resize);

    // メモリの再確保
    int *tmp = (int*)realloc(nums, sizeof(int) * resize);
    // メモリを再確保できなかった場合の処理
    if(tmp == NULL){
        free(nums);
        printf("メモリを再確保できませんでした!\n");
        exit(EXIT_FAILURE);
    }

    // 初期化
    for(int i = size; i < resize; i++){
        nums[i] = i+101;
    }

    // メモリが正常に再確保できたのでnumsに代入
    nums = tmp;

    // 出力
    for(int i = 0; i < resize; i++){
        printf("nums[%d]: %d\n", i, nums[i]);
    }
}

実行結果

要素数: 3
再確保する要素数: 6
nums[0]: 1
nums[1]: 2
nums[2]: 3
nums[3]: 104
nums[4]: 105
nums[5]: 106

まとめ

この記事では、C言語の動的配列について解説しました。

静的配列だけでなく動的配列を使うことで、配列の使える幅がとても増えると思います。状況によって使い分けましょう!

それでは今回の内容はここまでです!ではまたどこかで〜( ・∀・)ノ

最短3か月でエンジニア転職『DMM WEBCAMP COMMIT』