终止程序
程序终止不只有一种方式。C 标准库给出了从“完整清理退出”到“立刻终止”的一组接口,区别主要在于:会不会执行已注册回调、会不会刷新流、会不会保留更多清理步骤。
1. 正常终止:return 与 exit
从 main 返回,等价于调用 exit(status)。exit 会执行正常收尾流程:逆序调用 atexit 注册函数、刷新并关闭标准 I/O 流,并完成标准规定的其他终止清理步骤。只要程序需要“按常规流程退出”,这是首选路径。
#include <stdio.h>
#include <stdlib.h>
static void on_exit_1(void) { puts("on_exit_1"); }
static void on_exit_2(void) { puts("on_exit_2"); }
int main(void) {
if (atexit(on_exit_1) != 0 || atexit(on_exit_2) != 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}2
3
4
5
6
7
8
9
10
11
12
可能的输出(示例):
<输出与输入或平台相关,请以实际运行为准>
上例中回调按注册逆序执行,输出顺序是 on_exit_2 后 on_exit_1。
2. 快速终止:quick_exit 与 at_quick_exit
quick_exit(C11 起)用于“尽快结束但仍执行一小部分收尾”。它只调用 at_quick_exit 注册的回调,不调用 atexit 回调,也不要求执行完整的流刷新/关闭流程。适合需要有限清理、但不希望走完整退出链路的场景。
3. 立即终止:_Exit 与 abort
_Exit 会直接终止进程,不执行已注册回调;abort 会导致异常终止并触发 SIGABRT。这两条路径都不应被当成常规业务分支使用,它们更适合处理不可恢复错误或运行时严重故障。
4. 选择建议
若需要可预测清理,优先 return/exit;若只需有限回调,考虑 quick_exit;若状态已不可恢复,才使用 _Exit 或 abort。把退出语义写清楚,比“统一只用一种函数”更重要。
5. 退出码约定
终止接口都会向宿主环境返回状态码。最常见的可移植约定是使用 EXIT_SUCCESS 与 EXIT_FAILURE。若项目需要更细粒度错误码,可在内部统一映射,再在 main 出口集中转换,避免不同模块各自发明返回码语义。
6. 在清理函数里保持最小动作
无论是 atexit 还是 at_quick_exit 回调,都应避免复杂依赖链。回调越简单,退出路径越可预测;若回调里再调用可能失败的复杂逻辑,终止阶段的行为就会变得难以诊断。通常只保留必要释放与轻量状态收束,更容易保证一致性。