【6日でできるC言語入門】動的メモリの生成と消去

 C言語でプログラムを書くとき、メモリ管理は非常に重要なテーマです。特に「動的メモリの生成と消去」は、実用的なプログラムを作る上で避けては通れない技術です。
 動的メモリ管理を使うことで、必要なタイミングでメモリを確保したり、使い終わったら解放したりでき、柔軟なデータ構造や大量データの一時的な取り扱いが可能になります。
 ここでは、C言語における動的メモリの基本から、具体的な使用例、メモリリークの危険性、そして実践的なサンプルプログラムまで、図や表を用いて分かりやすく解説します。

1.メモリ領域の基礎知識

1.1. C言語プログラムのメモリ領域

C言語のプログラム実行時、OSは大きく4つのメモリ領域を割り当てます。

番号名前内容
プログラム領域マシン語(実行ファイルのコード)が入る領域
静的領域グローバル変数・static変数のための領域
ヒープ領域動的メモリ確保用領域(mallocなどで使用)
スタック領域関数のローカル変数や戻りアドレス等の領域

メモリ領域イメージ

  • プログラム領域 ・・・ 機械語プログラムが格納される。
  • 静的領域 ・・・ グローバル変数/static変数が置かれる。
  • ヒープ領域 ・・・ 動的にメモリを確保・消去する領域
  • スタック領域 ・・・関数の実行時に使われる(ローカル変数・戻り値管理)

1.2. ヒープ領域の特徴と役割

ヒープ領域は、プログラムの実行中に動的にメモリを確保・解放できる特別な領域です。

  • 必要なサイズの配列やデータを、実行時に確保できる
  • 使い終わったメモリは手動で解放する必要がある
  • 適切な管理を怠ると「メモリリーク」の原因になる

2.動的メモリの生成と消去

2.1. mallocとfreeの使い方

代表的なメモリ管理関数

関数名読み方主な用途と特徴
mallocマロック指定バイト分のメモリを動的確保
callocキャロックmallocと同等だが、確保領域を0初期化
reallocリアロック既存メモリ領域の再割当
freeフリー動的確保したメモリの解放

malloc/freeの基本例

プロジェクト/ファイル名: Lesson59_1/main.c

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

int main(void) {
    int *nums = NULL;
    int i;

    // メモリの動的確保(int型5つ分)
    nums = (int*)malloc(sizeof(int) * 5);
    if (nums == NULL) {
        printf("メモリの確保に失敗しました。\n");
        return 1;
    }

    // 値の代入
    for (i = 0; i < 5; i++) {
        nums[i] = (i + 1) * 10;
    }

    // 結果の表示
    printf("動的に確保した配列の内容:");
    for (i = 0; i < 5; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");

    // メモリの解放
    free(nums);

    return 0;
}

実行結果例

動的に確保した配列の内容:10 20 30 40 50

解説

  • malloc関数で必要なバイト数のメモリを確保
  • 戻り値はvoid型(「どんなデータであるか」が定義されていません。)なので、目的の型(ここではint)にキャスト
  • 確保失敗時はNULLが返るため、エラーチェックが必須
  • freeで解放しないと「メモリリーク」になる。

nums = (int*)malloc(sizeof(int) * 5); の意味

部分意味
malloc(sizeof(int) * 5)int型の大きさ(sizeof(int))×5個分のメモリ領域を動的に確保する。
(int*)mallocの戻り値(void*型)を、int*型(int型へのポインタ)に型変換(キャスト)する。
nums = ...確保したメモリの先頭アドレスをnums(int型ポインタ変数)に代入する。

2.2. メモリリークとその防止

メモリリークとは

用語意味
メモリリーク解放忘れなどで、誰も使わないメモリが残る現象
  • ヒープ領域のメモリはfree関数で明示的に解放が必要
  • 解放しないままプログラムが続くと、使えないメモリが積み重なり、最悪プログラムやOS全体が停止することもある

メモリリークのイメージ

3.実践例:動的配列とエラー処理

キーボードから配列の長さを入力し、その長さの整数配列を動的に生成。
1以上10以下の乱数を代入し表示するプログラム(失敗時のエラー処理あり)。

プロジェクト/ファイル名: Lesson59_2/main.c

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

int main(void) {
    int len, i;
    int* arr = NULL;

    printf("配列の長さを入力してください:");
    scanf("%d", &len);

    if (len <= 0) {
        printf("配列の長さは1以上にしてください。\n");
        return 0;
    }

    arr = (int*)malloc(sizeof(int) * len);
    if (arr == NULL) {
        printf("メモリの生成に失敗しました。\n");
        return 1;
    }

    srand((unsigned int)time(NULL));
    printf("生成した配列:");
    for (i = 0; i < len; i++) {
        arr[i] = rand() % 10 + 1;
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);

    return 0;
}

「SDLチェック」を「いいえ」にして、プログラムを実行します。

実行結果例1

配列の長さを入力してください:7
生成した配列:4 1 9 2 9 5 2

実行結果例2

配列の長さを入力してください:0
配列の長さは1以上にしてください。

実行結果例3(極端な値で確保失敗時)

※実行環境によっては、メモリを確保できます。大量に数値が表示されるので、「Ctrl」+「c」で中断します。

配列の長さを入力してください:999999999
メモリの生成に失敗しました。

まとめ

  • C言語プログラムは「ヒープ領域」を使い、実行時に柔軟なメモリ確保ができる
  • malloc で動的確保、free で解放を行う。エラー時のNULL判定も忘れずに
  • 解放忘れによるメモリリークに注意。特に長時間稼働するプログラムでは致命的になる場合がある
  • 配列サイズが実行時に決まる場合など、動的メモリ管理は非常に有効

 より複雑な用途や、より安全なメモリ管理をしたい場合には、callocreallocも併せて活用すると良いでしょう。