(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 函数返回的值),并指示程序执行状态。

常见实现

h 复制代码
// 实现决定
#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++ 标准

导致发生程序正常终止 ,但不完全清理资源

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

导致发生正常程序终止。

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

下面操作是未定义行为

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

🧨🧨atexit

atexit - cppreference.com

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

quick_exit - cppreference.com

在 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

abort - cppreference.com

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

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

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

某些编译器内建子程序:

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

示例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




相关推荐
初级代码游戏几秒前
国密起步6:GmSSL3使用SM4自定义格式加解密C++版
c++·国密·sm4
许野平2 分钟前
Rust 编译器使用的 C++ 编译器吗?
c++·rust
难以触及的高度13 分钟前
source ~/.bash_profile有什么用
开发语言·bash
DdddJMs__13522 分钟前
C语言 | Leetcode题解之第403题青蛙过河
c语言·数据结构·算法
骆晨学长23 分钟前
基于springboot学生健康管理系统的设计与实现
java·开发语言·spring boot·后端·spring
白总Server30 分钟前
php语言基本语法
开发语言·ide·后端·golang·rust·github·php
小林熬夜学编程33 分钟前
【Linux系统编程】第二十弹---进程优先级 && 命令行参数 && 环境变量
linux·运维·服务器·c语言·开发语言·算法
A懿轩A37 分钟前
MySQL SQL多表查询语句各种连接
java·开发语言·数据库·sql·mysql·mybatis
zls3653651 小时前
C# WPF中实现深拷贝的五种方式
开发语言·c#
六点半8881 小时前
【C/C++】涉及string类的经典OJ编程题
c语言·开发语言·c++·算法