メインコンテンツ

メモリ リーク

動的に割り当てられたメモリが解放されない

説明

この欠陥は、malloccallocrealloc、または new を介して割り当てられたメモリのブロックが解放されなかった場合に発生します。メモリが関数内で割り当てられる場合、以下の条件を満たせば欠陥は生じません。

  • 関数内で、free または delete を使用してメモリが解放される。

  • 関数が malloccallocrealloc または new によって割り当てられたポインターを返す。

  • 関数がグローバル変数またはパラメーターにポインターを格納する。

リスク

malloc などの動的メモリ割り当て関数はヒープ上のメモリを割り当てます。使用後にメモリを解放しないと、別の割り当てに使用できるメモリの量が減少します。メモリに制限がある組み込みシステムでは、プログラムの実行中でも、使用可能なヒープ メモリを使い果たす可能性があります。

修正方法

動的に割り当てられたメモリへのアクセスがあるスコープを確認します。このスコープの最後でメモリ ブロックを解放します。

メモリのブロックを解放するには、メモリの割り当て時に使用したポインターに対して関数 free を使用します。次に例を示します。

ptr = (int*)malloc(sizeof(int));
...
free(ptr);

同じモジュール内のメモリは同じ抽象化レベルで割り当てと解放を行うことをお勧めします。次の例の場合、func はメモリの割り当てと解放を同じレベルで行っていますが、func2 はそうではありません。

void func() {
  ptr = (int*)malloc(sizeof(int));
  {
    ...
  }
  free(ptr);
}

void func2() {
  {
   ptr = (int*)malloc(sizeof(int));
   ...
  }
  free(ptr);
}
CERT-C Rule MEM00-C を参照してください。

すべて展開する

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

void assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
         printf("Memory allocation failed");
         return;
        }


    *pi = 42;
    /* Defect: pi is not freed */
}

この例では、malloc によって pi が動的に割り当てられています。関数 assign_memory はメモリを解放せず、pi も返しません。

修正 — メモリを解放

1 つの修正方法として、pi が参照するメモリを関数 free を使用して解放することができます。関数 free は関数 assign_memory が終了する前に呼び出さなければなりません。

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

void assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
         printf("Memory allocation failed");
         return;
        }
    *pi = 42;

    /* Fix: Free the pointer pi*/
    free(pi);                   
}
修正 — 動的割り当てからポインターを返す

もう 1 つの修正方法は、ポインター pi を返すことです。pi を返すことにより、assign_memory を呼び出している関数が pi を使用してメモリ ブロックを解放できるようになります。

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

int* assign_memory(void)
{
    int* pi = (int*)malloc(sizeof(int));
    if (pi == NULL) 
        {
            printf("Memory allocation failed");
            return(pi);
        }
    *pi = 42;

    /* Fix: Return the pointer pi*/
    return(pi);                   
}
#define NULL '\0'

void initialize_arr1(void)
{
    int *p_scalar = new int(5);
}

void initialize_arr2(void)
{
    int *p_array = new int[5];
}

この例では、関数により 2 つの変数 p_scalar および p_array が、キーワード new を使用して作成されています。しかしこれらの関数は、これらのポインターについてメモリをクリーン アップせずに終了しています。これらの関数はこれらの変数の作成に new を使用したので、そのメモリは、各関数の最後で delete を呼び出してクリーン アップしなければなりません。

修正 — delete を追加

このエラーを修正するため、delete ステートメントを new の初期化ごとに追加します。大かっこ [] を使用して変数をインスタンス化した場合は、delete の呼び出しにも大かっこを使用しなければなりません。

#define NULL '\0'

void initialize_arrs(void)
{
    int *p_scalar = new int(5); 
    int *p_array = new int[5];  

    delete p_scalar;
    p_scalar = NULL;

    delete[] p_array;
    p_scalar = NULL;
}

結果情報

グループ: 動的メモリ
言語: C | C++
既定値: オフ
コマンド ライン構文: MEM_LEAK
影響度: Medium

バージョン履歴

R2013b で導入