[C言語のはじめ方] Part26: メモリの解放(free)

C言語

動的に確保したメモリの後片付け🧹

こんにちは!C言語学習、順調に進んでいますか?😊 Step 5ではファイル操作などを学びましたが、Step 6ではプログラムの安定性に欠かせない「メモリ管理」について深く掘り下げていきます。 今回は、malloc関数などで動的に確保したメモリを解放するためのfree関数について解説します。

malloc関数やcalloc関数、realloc関数を使って確保したメモリは、プログラムが自動で解放してくれません。使い終わったら必ずfree関数で解放する必要があります。これを怠ると、メモリリークという問題が発生し、プログラムやシステム全体の動作が不安定になる可能性があります。

free関数の基本的な使い方

free関数は、<stdlib.h> ヘッダファイルで定義されています。使い方はとてもシンプルです。

#include <stdlib.h> // free関数を使うために必要

void free(void *ptr);

引数 ptr には、malloc, calloc, realloc 関数が返したメモリブロックの先頭アドレス(ポインタ)を指定します。 指定されたポインタが指すメモリ領域が解放され、OSが再利用できるようになります。

ポイント💡:

  • free関数に渡すポインタは、動的に確保されたメモリの先頭アドレスである必要があります。
  • 引数 ptrNULL の場合、free関数は何もしません。エラーにもならないので、安全です。[1][2]
  • free関数自体は、解放するメモリのサイズを知る必要はありません。システムが内部で管理しています。

コード例で見てみよう 💻

実際にmallocでメモリを確保し、freeで解放する簡単な例を見てみましょう。

#include <stdio.h>
#include <stdlib.h> // malloc, free を使うために必要

int main(void) {
    int *ptr = NULL;
    int num_elements = 5;

    // int型5個分のメモリを動的に確保
    ptr = (int *)malloc(num_elements * sizeof(int));

    // メモリ確保が成功したかチェック (重要!)
    if (ptr == NULL) {
        fprintf(stderr, "メモリ確保に失敗しました。\n");
        return 1; // エラー終了
    }

    printf("メモリ確保成功!アドレス: %p\n", (void *)ptr);

    // 確保したメモリを使用する (例: 値を代入)
    for (int i = 0; i < num_elements; i++) {
        ptr[i] = i * 10;
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    // 使い終わったメモリを解放 ✨
    printf("メモリを解放します...\n");
    free(ptr);
    printf("メモリ解放完了。\n");

    // 【重要】解放後のポインタにはNULLを代入する
    ptr = NULL;

    // 解放後にアクセスしようとすると...? (未定義動作なのでコメントアウト推奨)
    // printf("解放後の値: %d\n", ptr[0]); // やってはいけない!

    return 0;
}

このコードでは、mallocで確保したメモリを使い終わった後にfree(ptr);で解放しています。

解放後のポインタへのアクセスは厳禁!
free関数を呼び出した後、そのポインタ変数(上記の例ではptr)には解放済みメモリのアドレスが残ったままです。この状態のポインタをダングリングポインタ(dangling pointer)と呼びます。
ダングリングポインタを使ってメモリにアクセスしようとすると、何が起こるか予測できません(未定義動作)。プログラムがクラッシュしたり、意図しないデータ書き換えが発生したりする可能性があります。[13]

対策: free関数を呼び出した直後に、ポインタ変数にNULLを代入する習慣をつけましょう。[10][13] これにより、誤ってダングリングポインタを使ってしまうリスクを減らすことができます。
free(ptr);
ptr = NULL; // 解放後はNULLを代入する!

freeを使う上での注意点 🚨

free関数は強力ですが、使い方を間違えると深刻なバグの原因となります。以下の点に注意しましょう。

注意点 説明 なぜ危険か 対策
二重解放 (Double Free) 同じメモリ領域を2回以上freeすること。 ヒープ管理領域が破壊され、未定義動作を引き起こします。プログラムのクラッシュや、悪意のあるコード実行に繋がる脆弱性となる可能性があります。[9][14]
  • 解放済みのポインタには必ずNULLを代入する。[9][13]
  • 複雑な条件分岐やループ内でfreeする場合は特に注意する。
  • プログラムの設計を見直し、メモリの所有権を明確にする。
不正なポインタの解放 malloc系関数で確保したメモリ以外のポインタをfreeすること。(例:静的配列のアドレス、スタック上の変数アドレス、初期化されていないポインタなど) 未定義動作を引き起こします。多くの場合、プログラムは即座にクラッシュします。[2] freeするポインタは、必ずmalloc, calloc, reallocが返したものであることを確認する。
解放済みメモリへのアクセス freeした後のメモリ領域に、ダングリングポインタ経由でアクセスすること。 未定義動作です。データが破壊されたり、プログラムがクラッシュしたりします。[8][13] 解放済みのポインタには必ずNULLを代入する。[10][13]
メモリリーク 確保したメモリを解放し忘れること。 プログラムが実行中に使用可能なメモリを徐々に消費し続け、最終的にはメモリ不足で動作が不安定になったり、停止したりします。[5][12]
  • mallocしたら、必ず対応するfreeをどこかで行うことを意識する。
  • エラー処理などで関数を抜ける際も、それまでに確保したメモリを解放する処理を忘れない。[15]
  • Valgrindなどのメモリデバッグツールを活用する。

まとめ

今回は、C言語におけるメモリ解放の要、free関数について学びました。

  • mallocなどで確保したメモリは、使い終わったら必ずfreeで解放する。
  • freeするポインタは、動的確保されたメモリの先頭アドレスでなければならない。
  • freeした後のポインタには、NULLを代入する習慣をつける(ダングリングポインタ対策)。
  • 二重解放不正なポインタの解放は絶対に避ける。

メモリ管理はC言語プログラミングにおいて非常に重要であり、少し複雑に感じるかもしれません。しかし、mallocfreeを正しくペアで使う意識を持つことが、安定したプログラムを作る第一歩です。💪

次は、NULLポインタのチェックについて学びます。お楽しみに!

参考情報

コメント

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