【C言語】
mallocの使い方
メモリリークを回避する方法

warning: memory leak

警告:メモリ解放漏れ
[-Wanalyzer-malloc-leak]


■メモリリークの例

#include    <malloc.h>
void    f1(void)
{
    char    *cp1 = malloc(100) ;
    if(cp1 == NULL) {
        return  ;
    }
    char    *cp2 = malloc(100) ;
    if(cp2 == NULL) {
        return  ;   // 先に確保済みの cp1 の解放忘れ!!
    }
    //  処理色々~~~~~~~
    //  後始末
    free(cp1);
    free(cp2);
}

malloc()で確保した領域を解放し忘れると解放漏れ(メモリリーク)が発生します。
この例では cp1 の解放漏れが発生します。


■メモリリーク対策(推奨しないdo-while(0))

#include    <malloc.h>
void    f2(void)
{
    char    *cp1 = NULL ;
    char    *cp2 = NULL ;
    do {
        cp1 = malloc(100) ;
        if(cp1 == NULL) {
            break ;
        }
        cp2 = malloc(100) ;
        if(cp2 == NULL) {
            break ; 
        }
    } while(0) ;
    //  処理色々~~~~~~~
    //  後始末
    if(cp1) free(cp1);
    if(cp2) free(cp2);
}

メジャールールで「関数の出口は一つだけ」と規定しているため、エラーが発生しても return ができないので 
do ~ while(0) で全体処理を囲ってエラーが発生したら break するスタイルを採用しているプロジェクトが多くあります。

このメジャールールは
以前は「必須」だったのですが
現在は「推奨」に降格しています。

本来[関数の出口一つ]はコードを読みやすくするために構造化定理で提案されたものですが、do~while(0)構文を使用したこのコードは
読みにくなっているので本末転倒と言えるでしょう。


■メモリリーク対策(推奨)

#include    <malloc.h>
void    f3(void)
{
    char *cp1 = NULL ;
    char *cp2 = NULL ;
    
    cp1 = malloc(100) ;
    if(cp1 == NULL) {
        goto err ;
    }
    cp2 = malloc(100) ;
    if(cp2 == NULL) {
        goto err ; 
    }
    //  処理色々~~~~~~
err:
    //  後始末
    if(cp1) free(cp1);
    if(cp2) free(cp2);
}

このように動的資源を複数回獲得した場合解放漏れが発生しやすいので 関数末尾にクリーナップ処理を設けそこにエラー発生時 goto で飛ぶ方法を推奨します。

注意事項:

cp1,cp2は関数の先頭でNULLで初期化する必要があります。
C99式に使用する直前で変数宣言すると
クリーンナップ処理で未初期化変数を参照する可能性があります。

参考:

CWE-416: Use After Free

C-FAQ7.20: 動的に割り当てた記憶領域は解放した後には使えないね。

MEM12-C. リソースの使用および解放の最中に発生するエラーが原因で関数を終了する場合に、Goto 連鎖の使用を検討する