atexit()函数登记顺序的的验证

本文介绍了C语言中的atexit函数,用于注册进程终止时调用的函数。按照ISO C标准,最多可登记32个函数,且调用顺序与登记顺序相反。内容包括函数介绍、用法及程序示例,详细解析了atexit在程序生命周期中的作用。

一、简介

1.、函数名: atexit

2、头文件:#include<stdlib.h>

3、功 能: 注册终止函数(即main执行结束后调用的函数)

4、用 法: void atexit(void (*func)(void));

5、注意:exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。

6、解析:按照ISO C的规定,一个进程可以登记至少32个函数,这些函数将由exit自动调用。atexit函数是一个登记函数, 相当于一个登记处, 让别人来注册, 一般而言, 一个进程可登记的函数个数是有限制的, 通常是32个函数被称为终止处理程序,并调用atexit函数来登记这些函数。通常这32个 在程序退出的时候, 登记处会自动回调用注册的函数。

    我们通常认为C语言的起始函数是main函数,实质上一个程序的启动函数并不一定是main函数,这个可以采用链接器来设置,但是gcc中默认main就是C语言的入口函数,在main函数启动之前,内核会调用一个特殊的启动例程,这个启动例程从内核中取得命令行参数值和环境变量值,为调用main函数做好准备,因此对应程序而言main函数并不是起始,但是对应C 语言而言,main函数就是入口地址,其他的链接器帮助我们完成,实际上mian函数的执行是使用了exec函数,这是一个函数族,这也是内核执行一个程序的唯一方法,这在进程控制部分将进行分析。

记得在面试题中有一道关于在main函数退出之后,是否还可以执行程序的问题,这时候就要使用到前面提到的atexit函数。

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

    其中,atexit的参数是一个函数地址,当调用此函数时无须传递任何参数,该函数也不能返回值,atexit函数称为终止处理程序注册程序,注册完成以后,当函数终止是exit()函数会主动的调用前面注册的各个函数,但是exit函数调用这些函数的顺序与这些函数登记的顺序相反,我认为这实质上是参数压栈造成的,参数由于压栈顺序而先入后出(即和函数调用的栈帧过程相似)。同时如果一个函数被多次登记,那么该函数也将多次的执行。
   
   我们知道exit是在main函数调用结束以后调用,因此这些函数的执行肯定在main函数之后,这也是上面面试题的解决方法。即采用atexit函数登记相关的执行函数即可。

   在exit函数的介绍中我们知道,exit()和_exit()以及_Exit()函数的本质区别是是否立即进入内核,_exit()以及_Exit()函数都是在调用后立即进入内核,而不会执行一些清理处理,但是exit()则会执行一些清理处理,这也是为什么会存在atexit()函数的原因,因为exit()函数需要执行清理处理,需要执行一系列的操作,这些终止处理函数实际上就是完成各种所谓的清除操作的实际执行体。atexit函数的定义也给了程序员一种运用exit执行一些清除操作的方法,比如有一些程序需要额外的操作,具体的清除操作可以采用这种方法对特殊操作进行清除等。

二、程序例

<span style="font-family:SimSun;font-size:18px;">  1#include <stdio.h>                                                          
  2 #include <stdlib.h>
  3 
  4 void f1(void)
  5 {
  6     printf("f1\n");
  7 }
  8 void f2(void)
  9 {
 10     printf("f2\n");
 11 }
 12 void f3(void)
 13 {
 14     printf("f3\n");
 15 }
 16 
 17 
 18 int main()
 19 {
 20     atexit(f3);
 21     atexit(f2);
 22     atexit(f1);
</span>

结果如下:


三、分析

    1、根据exit的执行过此可知,exit首先会调用各个终止处理程序,然后按需多次调用fclose(),关闭所有打开流,也就是说exit函数会执行一个标准I/O库的清理关闭操作:对所有打开的流调用fclose(),这样就会造成所有缓冲的输出数据都被冲洗即写入文件中。

   内核使程序执行的唯一方法是调用一个exec函数,进程自愿终止哦的唯一方法是显式或者隐式调用(通过exit函数)_exit()或者_Exit()函数。因此exit函数中实质是对_exit()或者_Exit()函数的封装。exit会先执行自定义的终止处理函数,然后执行I/O库函数清理函数fclose(),这也是为什么可以在终止处理函数中可以继续运用printf之类函数的原因,因为I/O库函数的流对象还没有被清除,当然可以继续运用。执行完了所有的fclose()以后,可以执行真正意义上的终止函数_exit()或者_Exit()函数。
   2、进程的终止方式:
 
  有8种方式使进程终止,其中前5种为正常终止,它们是
 
  1:从 main 返回
 
  2:调用 exit
 
  3:调用 _exit 或 _Exit
 
  4:最后一个线程从其启动例程返回
 
  5:最后一个线程调用 pthread_exit
 
  异常终止有3种,它们是
 
  6:调用 abort
 
  7:接到一个信号并终止
 
  8:最后一个线程对取消请求做出响应
 
  #include <stdlib.h?
 
  void exit (int status);
 
  void _Exit (int status);
 
  #include <unistd.h>
 
  void _exit (status);
 
  其中调用 _exit,_Exit 都不会调用终止程序
 
  异常终止也不会。

四、与析构函数调用的关系
    在atexit(f)调用之前被静态分配的对象的析构函数将在f()的调用之后被调用。在一个atexit(f)调用之后建立的这种对象的析构函数将在f的调用之前被调用。引自《The C++ Programming Language》(Bjarne Stroustrup)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值