我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言中花式退出程序的方式总结

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言中花式退出程序的方式总结

前言

在本篇文章当中主要给大家介绍C语言当中一些不常用的特性,比如在main函数之前和之后设置我们想要执行的函数,以及各种花式退出程序的方式。

main函数是最先执行和最后执行的函数吗

C语言构造和析构函数

通常我们在写C程序的时候都是从main函数开始写,因此我们可能没人有关心过这个问题,事实上是main函数不是程序第一个执行的函数,也不是程序最后一个执行的函数。

#include <stdio.h>
 
void __attribute__((constructor)) init1() {
  printf("before main funciton\n");
}
 
int main() {
  printf("this is main funciton\n");
}

我们编译上面的代码然后执行,输出结果如下图所示:

➜  code git:(main) ./init.out 
before main funciton
this is main funciton

由此可见main函数并不是第一个被执行的函数,那么程序第一次执行的函数是什么呢?很简单我们看一下程序的调用栈即可。

从上面的结果可以知道,程序第一个执行的函数是_start,这是在类Unix操作系统上执行的第一个函数。

那么main函数是程序执行的最后一个函数吗?我们看下面的代码:

#include <stdio.h>
 
void __attribute__((destructor)) __exit() {
  printf("this is exit\n");
}
 
void __attribute__((constructor)) init() {
  printf("this is init\n");
}
 
 
int main() {
  printf("this is main\n");
  return 0;
}

上面程序的输出结果如下:

➜  code git:(main) ./out.out 
this is init
this is main
this is exit

由此可见main函数也不是我们最后执行的函数!事实上我们除了上面的方法之外我们也可以在libc当中注册一些函数,让程序在main函数之后,退出执行前执行这些函数。

on_exit和atexit函数

我们可以使用上面两个函数进行函数的注册,让程序退出之前执行我们指定的函数

#include <stdio.h>
#include <stdlib.h>
 
void __attribute__((destructor)) __exit() {
  printf("this is exit\n");
}
 
void __attribute__((constructor)) init() {
  printf("this is init\n");
}
 
void on__exit() {
  printf("this in on exit\n");
}
 
void at__exit() {
  printf("this in at exit\n");
}
 
int main() {
  on_exit(on__exit, NULL);
  atexit(at__exit);
  printf("this is main\n");
  return 0;
}

this is init
this is main
this in at exit
this in on exit
this is exit

我们可以仔细分析一下上面程序执行的顺序。首先是执构造函数,然后执行 atexit 注册的函数,再执行 on_exit 注册的函数,最后执行析构函数。从上面程序的输出我们可以知道我们注册的函数生效了,但是需要注意一个问题,先注册的函数后执行,不管是使用 atexit 还是 on_exit 函数。我们现在看下面的代码:

#include <stdio.h>
#include <stdlib.h>
 
void __attribute__((destructor)) __exit() {
  printf("this is exit\n");
}
 
void __attribute__((constructor)) init() {
  printf("this is init\n");
}
 
void on__exit() {
  printf("this in on exit\n");
}
 
void at__exit() {
  printf("this in at exit\n");
}
 
int main() {
  // 调换下面两行的顺序
  atexit(at__exit);
  on_exit(on__exit, NULL);
  printf("this is main\n");
  return 0;
}

上面的代码输出如下:

this is init
this is main
this in on exit
this in at exit
this is exit

从输出的结果看确实和上面我们提到的规则一样,先注册的函数后执行。这一点再linux程序员开发手册里面也提到了。

但是这里有一点需要注意的是我们应该尽可能使用atexit函数,而不是使用on_exit函数,因为atexit函数是标准规定的,而on_exit并不是标准规定的。

exit和_exit函数

其中exit函数是libc给我们提供的函数,我们可以使用这个函数正常的终止程序的执行,而且我们在前面注册的函数还是能够被执行。比如在下面的代码当中:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void __attribute__((destructor)) __exit1() {
  printf("this is exit1\n");
}
 
void __attribute__((destructor)) __exit2() {
  printf("this is exit2\n");
}
 
 
void __attribute__((constructor)) init1() {
  printf("this is init1\n");
}
 
 
void __attribute__((constructor)) init2() {
  printf("this is init2\n");
}
 
void on__exit1() {
  printf("this in on exit1\n");
}
 
void at__exit1() {
  printf("this in at exit1\n");
}
 
void on__exit2() {
  printf("this in on exit2\n");
}
 
void at__exit2() {
  printf("this in at exit2\n");
}
 
 
int main() {
  // _exit(1);
  on_exit(on__exit1, NULL);
  on_exit(on__exit2, NULL);
  atexit(at__exit1);
  atexit(at__exit2);
  printf("this is main\n");
  exit(1);
  return 0;
}

上面的函数执行结果如下所示:

this is init1
this is init2
this is main
this in at exit2
this in at exit1
this in on exit2
this in on exit1
this is exit2
this is exit1

可以看到我们的代码被正常执行啦。

但是_exit是一个系统调用,当执行这个方法的时候程序会被直接终止,我们看下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
void __attribute__((destructor)) __exit1() {
  printf("this is exit1\n");
}
 
void __attribute__((destructor)) __exit2() {
  printf("this is exit2\n");
}
 
 
void __attribute__((constructor)) init1() {
  printf("this is init1\n");
}
 
 
void __attribute__((constructor)) init2() {
  printf("this is init2\n");
}
 
void on__exit1() {
  printf("this in on exit1\n");
}
 
void at__exit1() {
  printf("this in at exit1\n");
}
 
void on__exit2() {
  printf("this in on exit2\n");
}
 
void at__exit2() {
  printf("this in at exit2\n");
}
 
 
int main() {
  // _exit(1);
  on_exit(on__exit1, NULL);
  on_exit(on__exit2, NULL);
  atexit(at__exit1);
  atexit(at__exit2);
  printf("this is main\n");
  _exit(1); // 只改了这个函数 从 exit 变成 _exit
  return 0;
}

上面的代码输出结果如下所示:

this is init1
this is init2
this is main

可以看到我们注册的函数和最终的析构函数都没有被执行,程序直接退出啦。

花式退出

出了上面的_exit函数之外,我们还可以使用其他的方式直接退出程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h> 
 
void __attribute__((destructor)) __exit1() {
  printf("this is exit1\n");
}
 
void __attribute__((destructor)) __exit2() {
  printf("this is exit2\n");
}
 
 
void __attribute__((constructor)) init1() {
  printf("this is init1\n");
}
 
 
void __attribute__((constructor)) init2() {
  printf("this is init2\n");
}
 
void on__exit1() {
  printf("this in on exit1\n");
}
 
void at__exit1() {
  printf("this in at exit1\n");
}
 
void on__exit2() {
  printf("this in on exit2\n");
}
 
void at__exit2() {
  printf("this in at exit2\n");
}
 
 
int main() {
  // _exit(1);
  on_exit(on__exit1, NULL);
  on_exit(on__exit2, NULL);
  atexit(at__exit1);
  atexit(at__exit2);
  printf("this is main\n");
  syscall(SYS_exit, 1); // 和 _exit 效果一样
  return 0;
}

出了上面直接调用函数的方法退出函数,我们还可以使用内联汇编退出函数,比如在64位操作系统我们可以使用下面的代码退出程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h> 
 
void __attribute__((destructor)) __exit1() {
  printf("this is exit1\n");
}
 
void __attribute__((destructor)) __exit2() {
  printf("this is exit2\n");
}
 
 
void __attribute__((constructor)) init1() {
  printf("this is init1\n");
}
 
 
void __attribute__((constructor)) init2() {
  printf("this is init2\n");
}
 
void on__exit1() {
  printf("this in on exit1\n");
}
 
void at__exit1() {
  printf("this in at exit1\n");
}
 
void on__exit2() {
  printf("this in on exit2\n");
}
 
void at__exit2() {
  printf("this in at exit2\n");
}
 
 
int main() {
  // _exit(1);
  on_exit(on__exit1, NULL);
  on_exit(on__exit2, NULL);
  atexit(at__exit1);
  atexit(at__exit2);
  printf("this is main\n");
  asm(
    "movq $60, %%rax;"
    "movq $1, %%rdi;"
    "syscall;"
    :::"eax"
  );
  return 0;
}

上面是在64位操作系统退出程序的汇编实现,在64为系统上退出程序的系统调用号为60。下面我们使用32位操作系统上的汇编实现程序退出,在32位系统上退出程序的系统调用号等于1:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
 
void __attribute__((destructor)) __exit1() {
  printf("this is exit1\n");
}
 
void __attribute__((destructor)) __exit2() {
  printf("this is exit2\n");
}
 
 
void __attribute__((constructor)) init1() {
  printf("this is init1\n");
}
 
 
void __attribute__((constructor)) init2() {
  printf("this is init2\n");
}
 
void on__exit1() {
  printf("this in on exit1\n");
}
 
void at__exit1() {
  printf("this in at exit1\n");
}
 
void on__exit2() {
  printf("this in on exit2\n");
}
 
void at__exit2() {
  printf("this in at exit2\n");
}
 
 
int main() {
  // _exit(1);
  on_exit(on__exit1, NULL);
  on_exit(on__exit2, NULL);
  atexit(at__exit1);
  atexit(at__exit2);
  printf("this is main\n");
  asm volatile(
    "movl $1, %%eax;"
    "movl $1, %%edi;"
    "int $0x80;"
    :::"eax"
  );
  return 0;
}

以上就是C语言中花式退出程序的方式总结的详细内容,更多关于C语言退出程序的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言中花式退出程序的方式总结

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言中花式退出程序的方式总结

在本篇文章当中主要给大家介绍C语言当中一些不常用的特性,比如在main函数之前和之后设置我们想要执行的函数,以及各种花式退出程序的方式,需要的可以参考一下
2022-11-13

C语言格式输出二进制的2种方法总结

众所周知C中以八进制,十进制和十六进制都可以通过%o,%d和%x轻松实现,然而唯独没有提供二进制输出的快速方式,下面这篇文章主要给大家介绍了关于C语言格式输出二进制的2种方法,需要的朋友可以参考下
2022-11-13

C语言求素数的几种方式总结

这篇文章主要介绍了C语言求素数的几种方式总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-12-27

js中几种循环的退出方式实例总结

提到在一段程序中如果碰到需要终止,结束一个循环,函数或者一段代码,一般会想到以下这几个关键字return、continue、break,这篇文章主要给大家介绍了关于js中几种循环的退出方式,需要的朋友可以参考下
2022-12-08

C语言中分支和循环的6种实现形式总结

C语言时一门结构化的程序设计语言,这篇文章主要介绍了C语言中的分支和循环的6种实现形式,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
2023-05-15

嵌入式C语言轻量级程序架构内核编写的方法

这篇文章主要介绍了嵌入式C语言轻量级程序架构内核编写的方法的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇嵌入式C语言轻量级程序架构内核编写的方法文章都会有所收获,下面我们一起来看看吧。1.了解程序架构概念和作用
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录