【C言語】goto文でメモリ解放をシンプルにする

開発を行っていると、関数内で複数の動的メモリを確保しなければならないケースに遭遇します。

処理の途中でメモリ確保に失敗した場合、それまでに確保したメモリを解放しなければいけないため、どうしてもエラー処理が複雑になってしまいます。

ここでは、goto文を使用して、メモリ解放をシンプルにする方法について解説します。

目次

goto文とは

goto文は、関数内で指定したラベルへのジャンプを行うための構文です。以下のような形式を取ります。

goto <ラベル名>;

ラベル名は、関数内の任意の位置に指定することができる識別子です。goto文はこのラベル名に対応する箇所へジャンプします。

goto文は、プログラムの可読性や保守性を低下させる可能性があるため、使用を避ける傾向があります。

しかし、うまく活用することで、プログラムはよりシンプルになり、同時に可読性や保守性を向上させることができます。

メモリ解放を伴う複雑なエラー処理

複数の動的メモリを確保する処理では、メモリ確保に失敗した場合、それまでに確保したメモリを解放しなければいけないため、どうしてもエラー処理が複雑になってしまいます。

以下は、複雑なメモリ管理を行うコードの例です。

int example(void)
{
    int *p1, *p2, *p3;

    p1 = (int *)malloc(sizeof(int) * 10); /* p1のメモリ確保 */
    if (p1 == NULL)
    {
        return -1;
    }

    p2 = (int *)malloc(sizeof(int) * 10); /* p2のメモリ確保 */
    if (p2 == NULL)
    {
        free(p1); /* p1のメモリ解放 */
        return -1;
    }

    p3 = (int *)malloc(sizeof(int) * 10); /* p3のメモリ確保 */
    if (p3 == NULL)
    {
        free(p1); /* p1のメモリ解放 */
        free(p2); /* p2のメモリ解放 */
        return -1;
    }

    /* 処理 */

    free(p1); /* p1のメモリ解放 */
    free(p2); /* p2のメモリ解放 */
    free(p3); /* p3のメモリ解放 */

    return 0;
}

この例では、複数の動的メモリを順番に確保し、処理を行った後、最後に確保したメモリの解放を行っています。

また、メモリ確保に失敗した場合は、それまでに確保したメモリの解放を行っています。

このコードでも問題なく動作しますが、メモリ確保と解放が1対1で対応しておらず、メモリリークの検出が困難なため、可読性が良いとは言えません。

小規模なコードの場合、注意深く確認することでメモリリークの検出が可能ですが、大規模なコードの場合は、メモリリークの検出が困難になってしまいます。

goto文でメモリ解放をシンプルにする

goto文を利用すると、メモリ解放を1か所で行うことができるため、プログラムの可読性や保守性を向上させることができます。

以下は、先ほどの例をgoto文でシンプルにしたコードです。

int example(void)
{
    int ret = -1;
    int *p1 = NULL, *p2 = NULL, *p3 = NULL;

    p1 = (int *)malloc(sizeof(int) * 10); /* p1のメモリ確保 */
    if (p1 == NULL)
    {
        goto cleanup;
    }

    p2 = (int *)malloc(sizeof(int) * 10); /* p2のメモリ確保 */
    if (p2 == NULL)
    {
        goto cleanup;
    }

    p3 = (int *)malloc(sizeof(int) * 10); /* p3のメモリ確保 */
    if (p3 == NULL)
    {
        goto cleanup;
    }

    /* 処理 */

    ret = 0;
cleanup:
    free(p1); /* p1のメモリ解放 */
    free(p2); /* p2のメモリ解放 */
    free(p3); /* p3のメモリ解放 */

    return ret;
}

先ほどの例と同様に、複数の動的メモリを順番に確保し、処理を行った後、最後に確保したメモリの解放を行っています。

ただし、メモリ確保に失敗した場合、goto文を使用してメモリ解放処理までジャンプすることで、正常終了時のメモリ解放と処理を共通化させています。

これにより、メモリ確保と解放を1対1の関係にできるため、メモリリークの検出が目視でも簡単に行えるようになりました。

free()関数の引数がNULLの場合、何も処理を行わないことが言語仕様で定められています。

おわりに

goto文は、誤った使い方をした場合、可読性や保守性を低下させる可能性がありますが、適切に使用することで、処理をとてもシンプルにすることができます。

今回は、メモリ管理におけるエラー処理を例に挙げましたが、そのほかにも有効な活用方法が多数存在します。

状況に応じて使い分けることが重要です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次