Categories: 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(型) * 要素数);

alloca関数で確保したメモリはスコープから外れると自動的に解放されるようになっています。明示的に解放してはいけないので注意して下さい。

メモリの再確保

確保したメモリが足りなくなってしまった場合は、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言語の動的配列について解説しました。

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

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

ゆうまる

独学でプログラミングを勉強しているおじさん。いろんな言語を勉強したが浅く広くなためあまり仕事につながらない。また忘れっぽいため自分のブログを備忘録としても使っている。産まれてこのかたずっとネコを飼ってる生粋のネコ派。最近お腹が出てきて筋トレに奮闘中!

View Comments

Recent Posts

【Dart】コンストラクタのデフォルト引数について

Dartのコンストラクタのデフォルト引数…

2週間 ago

【Unity】有料アセットを無料で手に入れる方法

この記事では、Unityの有料アセットを…

4か月 ago

【Python】任意の秒数だけ処理を一時停止する方法【sleep()関数】

この記事では、Pythonで任意の秒数だ…

1年 ago

【Python】Wordの文書の新規作成と読み書き

この記事では、Pythonを使ってWor…

1年 ago

【Python】メタクラスって結局なんなの?

この記事では、Pythonのメタクラスに…

1年 ago