(C语言) stdlib 程序终止

💣前言

<stdlib.h> 是一个非常非常重要的库。重要到没有他就没有程序。

但由于太重要了,且涉及过多底层相关内容,一半初学不会强调这些内容。升值一些工作了一段时间在 CRUD 的程序员也不太了解。

本文重点整理C语言中在 stdlib 下有关 程序终止 相关的接口。

顺便回答大家一个一直关心的问题:

如果 main函数 不写return 0;会怎样?

main 函数返回时,无论是通过 return 语句还是抵达函数尾,都会将 return 语句的实参(或若使用隐式返回,则为 0)作为 exit_code 传递并执行 exit()

特别注意:笔者在 windowsmingw 测试 quick_exit & at_quick_exit 编译失败,linux 下编译成功。

💣程序终止

🧨EXIT_SUCCESS & EXIT_FAILURE

EXIT_SUCCESS, EXIT_FAILURE - cppreference.com

EXIT_SUCCESSEXIT_FAILURE 宏展开成能用作 exit 的实参的整数表达式(从而作为从 main 函数返回的值),并指示程序执行状态。

常见实现

// 实现决定
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

注意:EXIT_SUCCESS 和值0都能指示程序执行成功的状态,尽管并不要求 EXIT_SUCCESS 等于0。

main 函数,若正常结束,默认等效于 return 0;

🧨_Exit (C99)

_Exit - cppreference.com

在 C++11 中加入 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

exit - cppreference.com

导致发生正常程序终止。

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_codeEXIT_FAILURE,则返回指示不成功终止的实现定义状态。其他情况下返回实现定义的状态值。

下面操作是未定义行为

  • 若程序调用 exit 多于一次
  • 同时有调用 exitquick_exit
  • 在调用由 atexit 注册的函数期间,以 longjmp 退出该函数

🧨🧨atexit

atexit - cppreference.com

int atexit(void (*func)(void));

注册 func 所指向的函数,使它在程序正常终止(通过 exit() 或从 main() 返回)时得到调用。

  • 逆序调用注册成功的函数。
  • 可以注册同一函数多于一次。
  • 实现保证支持注册至少 32 个函数。确切的极限是由实现定义的。
  • 若注册成功则为 0,否则为非零值。

示例code

#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 的情况下,输出:

error opening file data.txt in function main()
atexit fun2
atexit fun1

有 data.txt 的情况下,输出:

Normal Return
atexit fun2
atexit fun1

🧨quick_exit & at_quick_exit (C11)

🧨🧨quick_exit

quick_exit - cppreference.com

在 C++11 中加入 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 ())时得到调用。

int at_quick_exit(void (*func)(void));  //(C11 起)s
  • at_quick_exit 线程安全,从多个线程调用此函数不会导入数据竞争。

  • 逆序调用注册成功的函数。

  • 实现保证支持注册至少 32 个函数。确切的极限是由实现定义。

  • 若注册成功则为 0,否则为非零值。

  • 所注册的函数在程序正常终止时并不会被调用

🧨abort

abort - cppreference.com

导致程序异常终止,除非传递给 signal 的信号处理函数正在捕捉 SIGABRT 且该处理函数不返回。

void              abort(void);  // (C11 前)
_Noreturn void    abort(void);  // (C11 起) (C23 前)
[[noreturn]] void abort(void);  // (C23 起)
  • 不调用传递给 atexit()的函数。
  • 实现定义:是否关闭打开的资源。
  • 向宿主环境返回指示不成功执行实现定义状态

POSIX 指定 abort() 函数撤除阻塞,或忽略 SIGABRT 信号。

某些编译器内建子程序:

能用于尽可能快地终止程序。

示例code

#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");
}

执行效果

Before abort()
Aborted



⭐END

🌟关注我

⭐交流方式⭐ |C/C++|算法|设计模式|软件架构-CSDN社区

关注我,学习更多C/C++,算法,计算机知识

B站:

👨‍💻主页:天赐细莲 bilibili




  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值