C语言中这么骚的退出程序方式你知道几个?

news2024/11/20 14:22:14

前言

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

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

1)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函数之后,退出执行前执行这些函数。

2)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并不是标准规定的。

3)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;
}

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

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

但是_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;
}

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

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

2、花式退出

除了上面的_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;
}

3、总结

在本篇文章当中主要给大家介绍C语言当中一些与程序退出的骚操作,希望大家有所收获!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/562376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python数据分析案例27——PCA-K均值-轮廓系数客户聚类

本案例适合应用统计&#xff0c;数据科学&#xff0c;电商专业 K均值对客户进行分类的案例都做烂了......但我认为这个案例还是有一定的价值的&#xff0c;使用了pca&#xff0c;还有轮廓系数寻找最优的聚类个数。 下面来看看 代码准备 导入包 import numpy as np import pa…

网上学影视后期靠谱吗 影视后期剪辑需要学什么

影视后期如果有人手把手当面教的话&#xff0c;当然是最好的。但很多人都没有这么好的条件&#xff0c;实际上&#xff0c;网上也有很多教程可以学习利用。不过&#xff0c;小伙伴们可能会有疑问&#xff0c;网上学影视后期靠谱吗&#xff0c;影视后期剪辑需要学什么&#xff1…

从创意造型到高品质曲面的卓越体验|CATIA ICEM Design Experience

目录 IDX为设计师提供了强大直观的建模工具 IDX为曲面工程师提供高品质数字模型处理能力 IDX与其他工具配合形成完整的数字化解决方案 建模是设计工作的重要环节&#xff0c;合适的数字模型能够在各个环节对整个设计流程产生正面的推动作用。 设计的不同阶段对模型有各自的…

Azkaban从入门到精通以及案例实操系列

1、Azkaban概论 1.1、Azkaban简介 Azkaban 是一个开源的基于 Web 的工作流调度系统&#xff0c;由 LinkedIn 公司开发并维护。它可以帮助用户在大规模数据处理中来管理和调度作业&#xff0c;提供了简单易用、高效可靠的工作流设计和调度功能。 Azkaban 的主要特点包括&…

亿发ERP系统,全链条采购协同管理数智化平台,中小企业采购业务全流程管理

在数字时代&#xff0c;中小型企业在采购管理方面面临多项挑战。 集采管理难&#xff1a;由于资源和专业知识有限&#xff0c;中小企业通常难以建立集中采购职能&#xff0c;无法有效简化整个组织的采购活动。这一挑战包括定义采购政策、标准化程序和实施高效的采购系统。 信…

『赠书活动 | 第六期』《“Java四大名著“,你集齐了吗?》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第六期』 本期书籍&#xff1a;《“Java四大名著”&#xff0c;你集齐了吗&#xff1f;》 赠书规则&#xff1a;评论区&#xff1a;点赞&…

供水管网监测系统,供水管网压力监测系统

水是城市赖以生存的血脉&#xff0c;直接关系到居民、企业和公共设施的正常生活和生产。然而&#xff0c;城市供水管网的高效运行面临着一系列挑战&#xff0c;如管道老化、泄漏和不均衡的供水压力等。 目前城市供水管网泄漏检测排查采用人工巡检、人工听漏的方式&#xff0c;巡…

Flutter3.10版本发布,编程语言的重大更新

Flutter是一款强大的跨端开发框架&#xff0c;可以帮助开发者构建高性能、美观、灵活的应用程序&#xff0c;从而实现跨平台开发和部署。小程序容器技术与跨端框架结合使用&#xff0c;为开发者提供一站式的小程序开发和发布服务&#xff0c;帮助他们更加轻松和高效地构建和部署…

CPU和GPU前端的应用

1、CPU&#xff08;英文Central Processing Unit 中央处理器&#xff09; CPU&#xff08;中央处理器&#xff09;是一种通用的处理器&#xff0c;其主要任务是执行计算机程序中的指令和序列。它能够处理复杂的逻辑判断、分支、跳转、内存访问等操作&#xff0c;因此在执行大多…

虚幻引擎4利用粒子系统实现物体轨迹描绘2- 消除轨迹

目录 前言粒子频繁产生对系统运行的影响轨迹的清除小结 前言 之前已经实现了UE4中跟随物体利用粒子系统产生轨迹的效果&#xff0c;文章链接如下&#xff1a; 虚幻引擎4利用粒子系统实现物体轨迹描绘_ADi_hhh的博客-CSDN博客 但是上篇文章还留下了两个问题 轨迹如何清除&am…

Spring探索——既生@Resource,何生@Autowired?

提到Spring依赖注入&#xff0c;大家最先想到应该是Resource和Autowired&#xff0c;很多文章只是讲解了功能上的区别&#xff0c;对于Spring为什么要支持两个这么类似的注解却未提到&#xff0c;属于知其然而不知其所以然。不知大家在使用这两个注解的时候有没有想过&#xff…

mysql8.0主从复制搭建

mysql8.0主从复制搭建 1.安装两个相同版本8.0的mysql数据库 主从IP端口主库192.168.139.1283306从库192.168.139.1303306 2.主从复制配置 2.1 修改mysql配置文件my.conf 主机mysql配置完整 [mysql] # 设置mysql客户端默认字符集 default-character-setutf8mb4 [client] # …

二十三种设计模式第十篇--外观模式

在现实生活中&#xff0c;常常存在办事较复杂的例子&#xff0c;如办房产证或注册一家公司&#xff0c;有时要同多个部门联系&#xff0c;这时要是有一个综合部门&#xff08;政务窗口 )能解决一切手续问题就好了。 软件设计也是这样&#xff0c;当一个系统的功能越来越强&…

2023年小型水库安全监测能力提升解决方案

一、方案背景 2023年小型水库安全监测能力提升试点项目建设将按照“统一规划、统一标准、统一实施、统一管理、统一支撑”的工作要求&#xff0c;对全区小型水库雨水情测报和监测设施建设项目按高水平、高标准、高质量要求开展安全监测能力提升建设&#xff0c;同步开展水工程安…

ZeLinAI是什么?国产ChatGPT快速搭建自己的AI应用

ChatGPT使用门槛高&#xff0c;需要科学上网短信接码等&#xff0c;不如直接选择国产ZelinAI&#xff0c;使用超简单轻轻松松从0到1零代码创建自己的AI应用。目前模型仅支持GPT-3.5-turbo&#xff0c;后续应该会接入文心一言、GPT-4、GPT-4.5和Bard&#xff0c;新手站长分享国产…

DL.to 最新研究(论文)推荐——分割、CVPR、扩散模型、感受野注意力模块

目录 一、CVPR 1.CrowdCLIP:基于视觉-语言模型的无监督人群计数 CrowdCLIP: Unsupervised Crowd Counting via Vision-Language Model 2.Beyond mAP&#xff1a;更好地评估实例分割 Beyond mAP: Re-evaluating and Improving Performance in Instance Segmentation with Se…

基于低代码平台的多租户解决方案

在云计算时代&#xff0c;“多租户”是一个非常重要的概念。根据百度百科中的定义&#xff0c;多租户技术是一种软件架构技术&#xff0c;简单来说是指以单一系统架构与服务提供多数客户端相同甚至可定制化的服务&#xff0c;并且仍然可以保障客户的数据在多租户环境中&#xf…

Redis之bitmap/hyperloglog/GEO

Redis之bitmap/hyperlog/GEO 一 面试题引入二 统计的类型三 hyperloglog3.1 行业术语3.2 hyperloglog基础3.2.1 基数3.2.2 定义3.2.3 基数统计3.2.4 基本命令 3.3 HyperLogLog原理3.3.1 去重复统计的方式3.3.2 原理 3.4 HyperLogLog案例实战3.4.1 需求3.4.2 方案讨论3.4.3 Hype…

五、SpringMVC从入门到入坟

一、SpringMVC概念 SpringMVC 是 Spring 框架中的一个模块&#xff0c;它是一个基于 MVC设计模式的 Web 框架&#xff0c;用于构建基于 Java 技术的 Web 应用程序。Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。 它的主要原理是将 Web 应用程序分成模型&…

实时频谱-2.4窗口函数

窗口函数 在离散傅立叶变换(DFT)分析运算中&#xff0c;一个固有的假设是要处理的数据是单个周期定期重复的信号。例如&#xff0c;在图2-8中的帧2上应用DFT处理时&#xff0c;信号上会进行周期性扩展。 在连续的帧之间一般会发生不连续点&#xff0c;如图 2-9 所示。 这些人…