コンテンツにスキップ

C言語/goto

出典: フリー教科書『ウィキブックス(Wikibooks)』

goto 文の理解と活用

[編集]

goto 文の基本概念

[編集]

C言語における goto 文は、プログラムの実行の流れを無条件に指定したラベルに移動させる制御構文です。goto 文は以下の形式で使用されます。

goto ラベル名;

そして、ジャンプ先となるラベルは以下のように定義します。

ラベル名:

シンプルな goto 文の例を見てみましょう。

#include <stdio.h>
int main() {
    printf("開始\n");
    goto jump_here;
    printf("この行は実行されません\n");
    
jump_here:
    printf("ここにジャンプしました\n");
    return 0;
}

このプログラムでは「この行は実行されません」という出力は表示されず、goto 文によって jump_here ラベルに直接ジャンプします。

goto 文の使用に関する議論

[編集]

goto 文はプログラムの流れを複雑にし、可読性を低下させるため、多くのプログラマーに避けられています。エドガー・ダイクストラの有名な論文「Go To Statement Considered Harmful(goto文は有害と考えられる)」以降、構造化プログラミングの考え方が広まり、goto 文の使用は減少しました。

しかし、goto 文が実用的で効率的なケースも確かに存在します。次の表は goto 文の一般的な評価をまとめたものです。

利点 欠点
複雑な制御フローの簡略化 プログラムの可読性低下
特定状況での効率向上 デバッグの困難さ
多重ループからの脱出 スパゲッティコードの原因
エラー処理の簡素化 構造化プログラミングの原則に反する

goto 文の実践的な使用例

[編集]

多重ループからの大域脱出

[編集]

goto 文の最も正当化される使用例の一つは、多重ループから即座に脱出する必要がある場合です。

#include <stdio.h>
int main() {
    int matrix[5][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 0, 14, 15},
        {16, 17, 18, 19, 20},
        {21, 22, 23, 24, 25}
    };
    
    printf("行列内でゼロを探します\n");
    
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            if (matrix[i][j] == 0) {
                printf("ゼロが見つかりました!位置: [%d][%d]\n", i, j);
                goto found_zero;
            }
        }
    }
    
    printf("ゼロは見つかりませんでした\n");
    goto end_search;
    
found_zero:
    printf("探索を終了します\n");
    
end_search:
    return 0;
}

この例では、行列内でゼロを見つけた時点で両方のループから一度に脱出しています。break 文では内側のループからしか脱出できませんが、goto 文を使用すれば両方のループから一度に脱出できます。

エラー処理と資源の解放

[編集]

複数のリソースを確保するコードでエラーが発生した場合、goto 文を使用して効率的にクリーンアップを行うことができます。

#include <stdio.h>
#include <stdlib.h>
int main() {
    FILE *file1 = NULL;
    FILE *file2 = NULL;
    int *buffer = NULL;
    
    // 最初のファイルを開く
    file1 = fopen("input.txt", "r");
    if (file1 == NULL) {
        printf("ファイル1を開けませんでした\n");
        goto cleanup;
    }
    
    // 二つ目のファイルを開く
    file2 = fopen("output.txt", "w");
    if (file2 == NULL) {
        printf("ファイル2を開けませんでした\n");
        goto cleanup;
    }
    
    // メモリを確保
    buffer = (int*)malloc(1000 * sizeof(int));
    if (buffer == NULL) {
        printf("メモリを確保できませんでした\n");
        goto cleanup;
    }
    
    // 正常に処理を実行
    printf("全てのリソースが正常に確保されました\n");
    // ここで実際の処理を行う
    
cleanup:
    // 確保されたリソースの解放
    if (buffer != NULL) free(buffer);
    if (file2 != NULL) fclose(file2);
    if (file1 != NULL) fclose(file1);
    
    return 0;
}

この例では、リソース確保の各段階でエラーが発生した場合、goto 文を使用して cleanup セクションに直接ジャンプし、それまでに確保されたリソースを適切に解放しています。

goto 文の代替手法

[編集]

goto 文を使わずに同等の機能を実現する方法も考えましょう。

多重ループ脱出の代替手法

[編集]
#include <stdio.h>
#include <stdbool.h>
int main() {
    int matrix[5][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10},
        {11, 12, 0, 14, 15},
        {16, 17, 18, 19, 20},
        {21, 22, 23, 24, 25}
    };
    
    bool found = false;
    int row = -1, col = -1;
    
    printf("行列内でゼロを探します\n");
    
    for (int i = 0; i < 5 && !found; i++) {
        for (int j = 0; j < 5 && !found; j++) {
            if (matrix[i][j] == 0) {
                found = true;
                row = i;
                col = j;
            }
        }
    }
    
    if (found) {
        printf("ゼロが見つかりました!位置: [%d][%d]\n", row, col);
        printf("探索を終了します\n");
    } else {
        printf("ゼロは見つかりませんでした\n");
    }
    
    return 0;
}

結論

[編集]

goto 文は、適切に使用すれば、特定の状況でコードを明確かつ効率的にする強力なツールになります。多重ループからの脱出やエラー処理のようなケースでは、goto 文の使用が正当化されることがあります。しかし、ほとんどの場合は構造化された制御フローを使用する方が良いでしょう。goto 文を使用する際は、コードの可読性と保守性に十分注意を払い、その使用が本当に必要かつ最適な解決策であるかを慎重に検討することが重要です。