
C言語基礎|配列の動的生成
これまでの配列は、だいたい次のどちらかでした。
- 要素数が最初から決まっている(int a[10]; のように固定)
- ある程度大きめに確保して、先頭の必要な分だけ使う
でも実際のプログラムでは、実行してみないと必要な要素数が分からないことが普通にあります。たとえば、
- ファイルから読み込んだ行数だけ配列が欲しい。
- ユーザーが入力した件数ぶんだけ記録したい。
- ネットワークから受け取ったデータサイズに合わせたい。
こういうときに使うのが、動的メモリ確保です。
配列の要素数を実行時に決めて、必要な分だけメモリを借りて、使い終わったら返す。これが「配列の動的生成」です。

静的配列・VLA・動的配列の違い
まずは整理しておくとスッキリします。
| 種類 | 例 | サイズ決定 | 置かれる場所のイメージ | 後片付け |
|---|---|---|---|---|
| 静的配列(固定長) | int a[100]; | コンパイル時 | スタック(main内など) | 不要(自動で消える) |
| VLA(可変長配列) | int a[n]; | 実行時 | スタック | 不要(自動で消える) |
| 動的配列(callocなど) | int *a = calloc(n, sizeof(int)); | 実行時 | ヒープ(借りる領域) | 必須(freeで返す) |
ポイントはここ
- 動的配列は free しないと返せない
- だから便利な分、責任も増える(でも実務ではこっちが基本になりがち)
calloc と free の役割(借りる・返す)
動的配列の基本ペアはこの2つです。
calloc の書式と意味
| 書式 | 意味 |
|---|---|
| void *calloc(size_t nmemb, size_t size); | nmemb 個の要素(1個あたり size バイト)の領域を確保し、全バイトを 0 で初期化して返す |
要点
- 第1引数:要素数
- 第2引数:要素1個の大きさ(sizeof(型) を使う)
- 戻り値:確保に成功すれば先頭アドレス、失敗すると NULL
free の書式と意味
| 書式 | 意味 |
|---|---|
| void free(void *ptr); | calloc などで確保した領域を解放する(返す) |
ポインタが「配列の先頭」を指す
動的配列は、変数そのものが配列になるわけではなく、配列の先頭を指すポインタを受け取ります。
図:動的配列の全体像(概念図)
| もの | 役割 | イメージ |
|---|---|---|
| int *a | 先頭要素を指す | a → a[0] |
| a[i] | i番目の要素にアクセス | 配列と同じ書き方で使える |
つまり、a はポインタだけど、a[i] という添字アクセスが使えるので、見た目は配列っぽく扱えます。
(実際は「ポインタ+添字演算子」でアクセスしています)
サンプルプログラム(温度ログを動的配列に保存して平均を出す)
温度(double)を n 個入力して、平均・最大・最小を出すプログラムを例に解説をします。
プロジェクト名:chap5-11-1 ソースファイル名:chap5-11-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
// double型の配列を動的に生成して、平均・最大・最小を表示
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n;
printf("記録する回数を入力してください:");
scanf("%d", &n);
if (n < 1 || n > 100000) {
puts("回数は 1〜100000 の範囲で入力してください。");
return 0;
}
double *temp = calloc(n, sizeof(double)); // n個のdouble配列を確保(0初期化)
if (temp == NULL) {
puts("メモリ確保に失敗しました。");
return 0;
}
puts("温度を入力してください。");
for (int i = 0; i < n; i++) {
printf("%d回目:", i + 1);
scanf("%lf", &temp[i]);
}
double sum = 0.0;
double max = temp[0];
double min = temp[0];
for (int i = 0; i < n; i++) {
sum += temp[i];
if (temp[i] > max) max = temp[i];
if (temp[i] < min) min = temp[i];
}
printf("平均:%.2f\n", sum / n);
printf("最大:%.2f\n", max);
printf("最小:%.2f\n", min);
free(temp); // 確保した領域を解放
return 0;
}このプログラムで学ぶポイント
| 行動 | 使っているもの | ねらい |
|---|---|---|
| 回数 n を入力 | scanf | 実行時に要素数を決める |
| temp を確保 | calloc(n, sizeof(double)) | n個分の領域を借りる(0初期化) |
| temp[i] に入力 | scanf + &temp[i] | 配列と同じ書き方で保存する |
| 平均・最大・最小 | for + if | 走査して集計する |
| 返却 | free(temp) | 借りた領域を返す |
ありがちなミスと安全な作法
動的配列は、慣れるまではここでつまずきがちです。
| ミス | 何が困る? | 対策 |
|---|---|---|
| temp が NULL なのに使う | 即クラッシュ級 | NULLチェックを必ずする |
| free を忘れる | メモリリーク(返し忘れ) | 使い終わったら必ず free |
| 要素数に変な値(負、巨大) | 異常動作・確保失敗・危険 | 入力値の範囲チェック |
| sizeof を間違える | 予想外のサイズで確保 | sizeof(型) を使う(sizeof(double)など) |
| free 後に temp[i] を使う | すでに返した領域を触る | free 後は使わない。必要なら temp = NULL |
calloc と malloc の違い(補足)
今回は calloc を中心にしました。理由はシンプルで、学習に向いているからです。
| 関数 | 初期化 | 使いどころ |
|---|---|---|
| calloc | 0で初期化される | 初期値が欲しい配列、学習向け |
| malloc | 初期化されない(不定値) | 高速寄り、必要なら自分で初期化 |
「初期値が0でいい配列」なら calloc は便利です。
