文章目录
- 💣前言
- 💣程序终止
-
- [🧨EXIT_SUCCESS & EXIT_FAILURE](#🧨EXIT_SUCCESS & EXIT_FAILURE)
- 🧨_Exit (C99)
- [🧨exit & atexit](#🧨exit & atexit)
- [🧨quick_exit & at_quick_exit (C11)](#🧨quick_exit & at_quick_exit (C11))
- 🧨abort
- ⭐END
💣前言
<stdlib.h>
是一个非常非常重要的库。重要到没有他就没有程序。
但由于太重要了,且涉及过多底层相关内容,一半初学不会强调这些内容。升值一些工作了一段时间在 CRUD 的程序员也不太了解。
本文重点整理C语言中在 stdlib
下有关 程序终止
相关的接口。
顺便回答大家一个一直关心的问题:
如果 main函数 不写
return 0;
会怎样?从
main
函数返回时,无论是通过return
语句还是抵达函数尾,都会将 return 语句的实参(或若使用隐式返回,则为 0)作为exit_code
传递并执行exit()
。
特别注意:笔者在 windows
用 mingw
测试 quick_exit & at_quick_exit
编译失败,linux
下编译成功。
💣程序终止
🧨EXIT_SUCCESS & EXIT_FAILURE
EXIT_SUCCESS
和 EXIT_FAILURE
宏展开成能用作 exit 的实参的整数表达式(从而作为从 main
函数返回的值),并指示程序执行状态。
常见实现
h
// 实现决定
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
注意:EXIT_SUCCESS
和值0都能指示程序执行成功的状态,尽管并不要求 EXIT_SUCCESS
等于0。
main 函数,若正常结束,默认等效于 return 0;
🧨_Exit (C99)
在 C++11 中加入 C++ 标准
导致发生程序正常终止 ,但不完全清理资源。
c
void _Exit(int exit_code); // (C99 起) (C11 前)
_Noreturn void _Exit(int exit_code); // (C11 起) (C23 前)
[[noreturn]] void _Exit(int exit_code); // (C23 起)
- 不调用 传递给
at_quick_exit()
或atexit()
的函数。 - 实现定义:是否将未写入数据冲入打开的流、关闭打开的流或移除临时文件。
exit_code
的值:
- 为 0 或
EXIT_SUCCESS
:则将指示成功终止的状态返回给宿主环境。 - 为
EXIT_FAILURE
:则返回指示不成功终止的实现定义状态。 - 其他:即实现定义。
🧨exit & atexit
🧨🧨exit
导致发生正常程序终止。
c
void exit(int exit_code); // (C11 前)
_Noreturn void exit(int exit_code); // (C11 起) (C23 前)
[[noreturn]] void exit(int exit_code); // (C23 起)
进行几个清理步骤:
- 以注册的逆序调用 传递给 atexit 的函数
- 冲入并关闭所有 C 流
- 移除 tmpfile 创建的文件
- 将控制返回给宿主环境 。若
exit_code
为零或 EXIT_SUCCESS,则返回指示成功终止的实现定义状态。若exit_code
为 EXIT_FAILURE,则返回指示不成功终止的实现定义状态。其他情况下返回实现定义的状态值。
下面操作是未定义行为:
- 若程序调用
exit
多于一次 - 同时有调用
exit
和quick_exit
- 在调用由
atexit
注册的函数期间,以 longjmp 退出该函数
🧨🧨atexit
c
int atexit(void (*func)(void));
注册 func
所指向的函数,使它在程序正常终止(通过 exit()
或从 main()
返回)时得到调用。
- 逆序调用注册成功的函数。
- 可以注册同一函数多于一次。
- 实现保证支持注册至少 32 个函数。确切的极限是由实现定义的。
- 若注册成功则为 0,否则为非零值。
示例code
c
#include <stdio.h>
#include <stdlib.h>
void fun1(void) {
puts("atexit fun1");
}
void fun2(void) {
puts("atexit fun2");
}
int main(void) {
atexit(fun1);
atexit(fun2);
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
fprintf(stderr, "error opening file data.txt in function main()\n");
exit(EXIT_FAILURE);
} else {
fclose(fp);
}
printf("Normal Return\n");
return EXIT_SUCCESS;
}
在没有 data.txt 的情况下,输出:
basg
error opening file data.txt in function main()
atexit fun2
atexit fun1
有 data.txt 的情况下,输出:
bash
Normal Return
atexit fun2
atexit fun1
🧨quick_exit & at_quick_exit (C11)
🧨🧨quick_exit
在 C++11 中加入 C++ 标准
导致程序正常终止 ,而不完全清理资源。
c
_Noreturn void quick_exit(int exit_code); // (C11 起) (C23 前)
[[noreturn]] void quick_exit(int exit_code); // (C23 起)
效果流程:
- 以注册顺序的逆序调用 传递给
at_quick_exit
的函数。 - 调用
_Exit(exit_code)
。
说白了就是不做额外处理的 exit()
。
🧨🧨at_quick_exit
at_quick_exit - cppreference.com
在 C++11 中加入 C++ 标准
注册 func
所指向的函数,使它在程序正常终止(通过 quick_exit ()
)时得到调用。
c
int at_quick_exit(void (*func)(void)); //(C11 起)s
-
at_quick_exit
线程安全,从多个线程调用此函数不会导入数据竞争。 -
逆序调用注册成功的函数。
-
实现保证支持注册至少 32 个函数。确切的极限是由实现定义。
-
若注册成功则为 0,否则为非零值。
-
所注册的函数在程序正常终止时并不会被调用。
🧨abort
导致程序异常终止 ,除非传递给 signal 的信号处理函数正在捕捉 SIGABRT 且该处理函数不返回。
c
void abort(void); // (C11 前)
_Noreturn void abort(void); // (C11 起) (C23 前)
[[noreturn]] void abort(void); // (C23 起)
- 不调用传递给
atexit()
的函数。 - 实现定义:是否关闭打开的资源。
- 向宿主环境返回指示不成功执行 的实现定义状态。
POSIX 指定 abort()
函数撤除阻塞 ,或忽略 SIGABRT
信号。
某些编译器内建子程序:
__builtin_trap
(gcc、clang 及 icc)__fastfail
/__debugbreak
(msvc)
能用于尽可能快地终止程序。
示例code
c
#include <stdio.h>
#include <stdlib.h>
void fun_1(void) {
puts("atexit fun1");
}
void fun_q1(void) {
puts("at_quick_exit fun1");
}
int main(void) {
atexit(fun_1);
at_quick_exit(fun_q1);
puts("Before abort()");
abort();
puts("After abort()");
printf("Normal Return\n");
}
执行效果
bash
Before abort()
Aborted
⭐END
🌟关注我
⭐交流方式⭐ |C/C++|算法|设计模式|软件架构-CSDN社区
关注我,学习更多C/C++,算法,计算机知识
B站:
👨💻主页:天赐细莲 bilibili