C言語

【C言語】第3章第15回:メモリリークを防ぐ配列操作

メモリリークは、動的メモリ管理を伴うプログラムで発生しやすい問題です。この章では、C言語における配列操作でのメモリリークを防ぐ方法を学びます。

1. メモリリークとは?

メモリリークは、プログラムが動的に割り当てたメモリを解放しないまま参照を失うことで発生します。

メモリリークが引き起こす問題:

  • システムのパフォーマンス低下
  • プログラムのクラッシュや動作異常
  • メモリリソースの枯渇

これを防ぐためには、free関数を適切に使用することが重要です。

2. 動的配列でのメモリリークの例

例:解放を忘れたプログラム

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

int main() {
    int *arr = (int *)malloc(5 * sizeof(int)); // 動的にメモリを割り当て

    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1; // 配列を初期化
        printf("%d ", arr[i]);
    }
    printf("\n");

    // free(arr); // メモリを解放しないため、リークが発生

    return 0;
}

解説:

  • 動的に割り当てたメモリはfreeで解放する必要があります。
  • 解放を忘れると、プログラム終了時までメモリが確保されたままになります。

3. メモリリークを防ぐ基本ルール

動的メモリを安全に管理するために、次のルールを守りましょう:

  • malloccallocで割り当てたメモリは、必ずfreeで解放する。
  • 解放後のポインタにNULLを代入して、二重解放を防ぐ。
  • エラー処理時にも適切にメモリを解放する。

4. メモリ解放の正しい例

例:動的配列を安全に解放するプログラム

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

int main() {
    int *arr = (int *)malloc(5 * sizeof(int));

    if (arr == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr); // メモリを解放
    arr = NULL; // ポインタをNULLにする

    return 0;
}

解説:

  • free(arr)で確保されたメモリを解放します。
  • 解放後にarr = NULLを設定して、不正なメモリアクセスを防ぎます。

5. 大量の動的配列を扱う場合の注意点

複数の動的配列を扱う場合、解放漏れが起こりやすくなります。以下のコードでその例を示します:

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

int main() {
    int **matrix = (int **)malloc(3 * sizeof(int *));
    if (matrix == NULL) {
        printf("Memory allocation failed\n");
        return 1;
    }

    for (int i = 0; i < 3; i++) {
        matrix[i] = (int *)malloc(4 * sizeof(int));
        if (matrix[i] == NULL) {
            printf("Memory allocation failed for row %d\n", i);
            // 解放処理を追加
            for (int j = 0; j < i; j++) {
                free(matrix[j]);
            }
            free(matrix);
            return 1;
        }
        for (int j = 0; j < 4; j++) {
            matrix[i][j] = i * 4 + j + 1;
        }
    }

    // 配列の内容を表示
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    // メモリを解放
    for (int i = 0; i < 3; i++) {
        free(matrix[i]);
    }
    free(matrix);

    return 0;
}

解説:

  • 二次元配列を動的に割り当てた場合、各行を個別に解放する必要があります。
  • エラーが発生した場合でも、すでに割り当てられたメモリを忘れず解放します。

6. 練習問題

以下の課題に挑戦して、メモリ管理を練習しましょう。

  1. 動的配列を作成し、ユーザーが入力した要素を格納した後、正しく解放するプログラムを作成してください。
  2. ユーザーが指定するサイズの二次元配列を動的に割り当て、すべての要素を0で初期化してから解放するプログラムを作成してください。
  3. 複数の動的配列を扱う場合のエラー処理を追加したプログラムを作成してください。

7. まとめ

動的配列を使用する際には、メモリリークを防ぐための適切な管理が必要です。free関数を活用し、不要になったメモリを必ず解放する習慣を身につけましょう。