Skip to content

Memory — malloc / realloc / free

TL;DR

確保の失敗を確認し、所有者が不要になった領域を一度だけ free します。realloc の結果は一時ポインタで受けます。以下はISO C99以降、要求サイズが正の場合の例です。

Example

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

int main(void)
{
    size_t count = 4;
    int *values;
    if (count > SIZE_MAX / sizeof *values) {
        return EXIT_FAILURE;
    }
    values = malloc(count * sizeof *values);
    if (values == NULL) {
        return EXIT_FAILURE;
    }
    for (size_t i = 0; i < count; ++i) {
        values[i] = (int)i;
    }

    size_t next_count = 8;
    if (next_count > SIZE_MAX / sizeof *values) {
        free(values);
        return EXIT_FAILURE;
    }
    int *next = realloc(values, next_count * sizeof *values);
    if (next == NULL) {
        free(values); /* 正のサイズでの失敗時、元の領域は有効 */
        return EXIT_FAILURE;
    }
    values = next;
    for (size_t i = count; i < next_count; ++i) {
        values[i] = 0;
    }
    printf("%d %d\n", values[3], values[7]); /* 3 0 */
    free(values);
    return EXIT_SUCCESS;
}

Explanation

  • malloc で確保した内容は未初期化です。読む前に書き込みます。
  • calloc は確保領域の全ビットを0にします。あらゆる型の意味上のゼロやNULLポインタ表現を保証するものではありません。
  • 正のサイズで realloc が失敗すると元の領域が残ります。成功したら返されたポインタを使い、元の領域を指していた別のポインタも使い続けないでください。
  • Cでは malloc の戻り値のキャストは不要です。<stdlib.h> の宣言は必要です。

Pitfalls

NGは values = realloc(values, bytes); と直接代入することです。失敗時に元のポインタを失うため、上の例のように一時変数で受けます。

所有権と寿命

二重解放、解放後の読み書き(use-after-free)は未定義動作です。free(NULL) は何もしませんが、解放後に1つの変数をNULLにしても、その領域を指す別のポインタは無効なままです。

要素数と要素サイズの積は、確保前にオーバーフローを確認します。ゼロサイズの確保・再確保には規格バージョンや処理系に依存する点があるため、この例では正のサイズだけを扱います。

参考

C11委員会草案 N1570 §7.22.3。ゼロサイズの realloc を含む後続規格の変更を、この例の前提に混在させていません。