很多C/C++初学者都有一个固定认知:main函数return,程序就结束了。
其实不对!main返回只是代表主函数执行完毕,进程的收尾工作,由C运行库CRT接管, atexit 注册的回调函数,就是在 main 结束之后才执行。
示例代码
#include<stdlib.h>
#include<stdio.h>
void fn1(void);
void fn2(void);
int main(void)
{
atexit(fn1);
atexit(fn2);
printf("main exit...\n");
return 0;
}
void fn1()
{
printf("calling fn1()...\n");
}
void fn2()
{
printf("calling fn2()...\n");
}
运行输出
main exit...
calling fn2()...
calling fn1()...
核心知识点
- 调用顺序:后进先出(栈的顺序)
-
先注册 fn1 ,再注册 fn2
-
main结束后,后注册的先执行:先跑fn2,再跑fn1
-
最多可以注册32个atexit回调函数
- 执行时机:main函数return之后,进程真正销毁之前
main 函数里的 return 0 ,只是从主函数返回,并不是进程直接退出。CRT启动代码拿到main返回值之后,才会依次调用atexit注册的清理函数,全部执行完成,才会调用系统调用,结束进程。
- 重要限制:只有【正常终止】才会执行atexit回调
✅ 触发场景:main正常return、代码调用 exit()
❌ 不会触发: Ctrl+C 信号终止、 _exit() 、kill -9、程序崩溃段错误。
如果按下Ctrl+C想执行清理函数,需要手动捕获SIGINT信号,在信号处理函数内部调用exit(),才会触发atexit。
实际开发用处
atexit 适合做程序退出时的资源清理:关闭文件句柄、释放全局堆内存、保存配置、日志刷盘。
注意:工业嵌入式程序慎用,信号异常退出时atexit不会跑,关键资源清理不能只依赖它。