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

C言語

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

動的配列とは、動的に確保したメモリを使った配列のことを言います。メモリの確保数を変更することで配列の要素数を動的に変更することができます。ちなみに、要素数が固定されている配列のことを静的配列と呼びます。

メモリの動的な確保

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

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

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

#include <stdlib.h>

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

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

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

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

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

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

alloca関数(アロカ)
※ 標準関数では無い

alloca関数は、malloc関数と同様に使用します。

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

メモリの再確保

確保したメモリが足りなくなってしまった場合、realloc関数を使ってメモリを再確保することができます。ただし、メモリを拡張するのではなく、新しくメモリを確保し、そこにデータをコピーするのでサイズが大きいほど再確保にコストがかかってしまいます。

使い方

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

ポインタ = (型*)realloc(ポインタ, sizeof(型) * 要素数);
POINT
  • 直接ポインタに再確保したメモリを渡すと、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(ポインタ);
POINT
  • メモリを解放せずに確保し続けてしまうことをメモリリークと呼びます
  • 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言語の動的配列について解説しました。

今回のおさらい

  • stdlib.hをインクルードする
  • malloc関数 = 『動的にメモリを確保する』
  • calloc関数 = 『動的にメモリを確保し、ゼロクリアする』
  • alloca関数 = 『メモリをスタックから取る。スコープから外れる時に自動的に解放される』
  • realloca関数 = 『メモリを再確保する』
  • free関数 = 『メモリを解放する』

静的配列だけでなく動的配列を使うことで、配列の使える幅がとても増えると思います。

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

タイトルとURLをコピーしました