goto 可在同一函数内跳转到带标签的语句。日常控制流宜使用结构化语句;在 C 语言缺少异常机制的情况下,将多处失败路径汇聚到单一清理点,是 goto 的常见合理用途,常与错误处理及动态内存释放配合。

goto 语法

goto label;

label:
    /* 跳转目标 */

滥用 goto 会使控制流难以跟踪;宜限制在错误恢复等少数模式中使用。

简单分支示例

除零时跳转到统一处理代码:

#include <stdio.h>

int main(void) {
    int numerator = 10;
    int denominator = 0;
    int result;

    if (denominator == 0) {
        goto excep;
    }

    result = numerator / denominator;
    printf("%d\n", result);

excep:
    printf("Exception: Division by zero is not allowed!\n");
    return 0;
}

单一清理出口

打开文件、读数据等多步操作中,任一步失败时均可跳转到同一标签释放资源:

#include <stdio.h>

int main(void) {
    FILE *file = NULL;
    int result = 0;

    file = fopen("example.txt", "r");
    if (file == NULL) {
        printf("Error opening file\n");
        goto error;
    }

    result = fread(NULL, 1, 100, file);
    if (result == 0) {
        printf("Error reading file\n");
        goto error;
    }

    printf("Successful\n");

error:
    if (file != NULL) {
        fclose(file);
    }
    return 0;
}

与在每个分支重复编写 fclose 相比,集中清理可降低遗漏释放的概率。

其他合理用途

  • 多步 malloc 失败时跳转到统一 free 序列。
  • 从深层嵌套循环中跳出外层(亦可权衡是否重构为函数)。
  • 在大型函数末尾统一返回前的收尾逻辑。

使用限制

  • 不宜用 goto 替代常规的循环与条件结构。
  • 跳转不得越过变量的初始化进入其作用域(C99 及以后规则须遵守)。
  • 跨函数跳转须使用 setjmp / longjmp 等机制,与 goto 不同。

本节说明了 goto 在错误清理中的典型模式。下一节介绍信号的捕获与处理。