C言語入門|malloc と free による動的メモリ管理

ここまでで、
「スタック上の配列にはサイズと寿命の限界がある」
という事実を学びました。

じゃあ、
関数を越えて使える配列
実行時にサイズが決まる配列
は、どうやって扱えばいいのでしょうか?

そこで登場するのが、
C言語における 動的メモリ管理 の主役、

malloc と free

です。

この2つを正しく使えるようになると、
C言語で扱えるデータの自由度が一気に広がります。

ヒープ領域を使うという発想

malloc が確保するメモリは、
これまで使ってきたスタック領域ではありません。

スタックとヒープの違い(おさらい)

項目スタックヒープ
確保タイミング関数開始時実行中に任意
解放タイミング関数終了時明示的に解放
サイズ小さい比較的大きい
管理自動プログラマ

malloc は、
ヒープ領域に連続したメモリ領域を確保する命令
です。

malloc ― ヒープにメモリを確保する

まずは基本となる関数から見ていきましょう。

malloc の書式

void* malloc(size_t len);

引数と戻り値

項目内容
len確保したいバイト数
戻り値確保された領域の先頭アドレス
失敗時ヌルポインタ

malloc は、

  • 「int を何個」
    ではなく
  • 「何バイト」

を指定してメモリを確保する点が重要です。

malloc が返すポインタの正体

malloc の戻り値は void* 型です。

これは、

どんな型としても使える汎用ポインタ

という意味を持っています。

実際の利用では、
用途に応じて int* や char* にキャストして使います。

int* p = (int*) malloc(4 * 10);

malloc だけでは終われない理由

ここで、とても大事なルールがあります。

malloc を使ったら、必ず free を使う

通常の配列は、
関数が終わると自動的に寿命が尽きます。

しかし、
malloc で確保したヒープ領域は 自動では解放されません

free ― ヒープ領域を解放する

使い終わったヒープ領域は、
必ず free で解放します。

free の書式

void free(void* p);

引数

項目内容
p過去に malloc で確保した先頭アドレス

free を呼び出すことで、

  • ヒープ領域が解放され
  • 再利用可能な状態

になります。

解放し忘れるとどうなる?

free を忘れると、
確保したメモリは 永遠に使われたまま になります。

これを メモリリーク と呼びます。

メモリリークの影響

  • プログラムが長時間動くほど
  • 使用メモリが増え続け
  • 最終的に異常終了

サーバプログラムや常駐アプリでは、
致命的な問題になります。

ヒープを使った安全な配列生成例

先ほどの「危険な配列返却」を、
ヒープを使って書き直してみましょう。

サンプルプログラム

プロジェクト名:10-10-1 ソースファイル名: sample10-10-1.c

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

int* createScores(void)
{
    int* scores = (int*) malloc(4 * sizeof(int));
    return scores;
}

int main(void)
{
    int* p = createScores();

    if (p == NULL) {
        printf("メモリ確保に失敗しました\n");
        return 1;
    }

    p[0] = 75;
    printf("確保したアドレスは %p です\n", p);

    free(p);   // 使い終わったら必ず解放
    return 0;
}

実行結果(例)

確保したアドレスは 0x55b1c8f2a2a0 です

なぜヌルポインタを確認するのか

malloc は、
メモリ確保に失敗すると ヌルポインタ を返します。

ヌルポインタとは

  • アドレス 0 を指す特別なポインタ
  • 「何も指していない」ことを表す

C言語では伝統的に、

  • NULL
  • (void*)0

が使われてきました。

近年の C23 以降では、
nullptr の利用が推奨されています。

ここで押さえておきたい nullptr の正体

nullptr は、
「何も指していない」ことを表す特別なポインタ値です。

  • アドレスの 0 番地を指すポインタ
  • メモリ確保の失敗を表すために使われる
  • 「有効なメモリではない」ことを示す合図

なぜ nullptr が推奨されるのか

従来の NULL は、

  • 単なる整数 0 と区別がつきにくい
  • 文脈によって意味が曖昧になる

という問題がありました。

そのため C23 以降のC言語 では、

ポインタ専用の値として nullptr を使う

ことが推奨されています。

0番地が特別扱いされる理由

コンピュータの世界では、

アドレス0付近は、どんな用途にも使わない

という暗黙のルールがあります。

そのため、

  • malloc が有効なメモリを返す
    → 0番地になることはない
  • 0 が返る
    → 確保失敗

と判断できるわけです。

calloc という選択肢もある

malloc によく似た関数として、
calloc があります。

calloc の特徴

  • malloc とほぼ同じ用途
  • 確保直後に、全バイトを 0 で初期化

つまり、

  • malloc + memset(0)
  • 1行で済ませたもの

と考えると分かりやすいです。

プロジェクトによっては、

  • 安全性を重視して
  • malloc を禁止し
  • 常に calloc を使う

というルールが設けられることもあります。

動的メモリ管理で一番大切な考え方

malloc と free を使うとき、
一番大切なのは次の意識です。

確保した人が、必ず解放する

これを守れないと、

  • メモリリーク
  • ダングリングポインタ
    →メモリを解放した後も、その解放されたメモリ領域を指し続けている「宙ぶらりん」の状態のポインタ
  • 二重解放

といった、深刻なバグにつながります。

まとめ:malloc と free は力を与える道具

malloc と free は、

  • 配列の限界を超える。
  • プログラムを柔軟にする。

非常に強力な道具です。

その一方で、

  • 自動では守ってくれない。
  • すべて自己責任

という、C言語らしい厳しさも持っています。

ここを乗り越えれば、
C言語の核心に一歩踏み込んだ
と言ってもいいでしょう。