C语言程序运行时的一些细节
本章可以看作是 《Unix 环境高级编程》Ch7 的笔记。
C 程序运行的开始和结束
一个可以运行的 C 语言总要有一个 main 函数,main 函数现在的完整定义是 int main(int argc, char *argv[] ) ,现在的 C 语言已经不允许写成 void main() 的形式了,main 函数必须有返回值,如果 main 函数没有返回值,那么编译器会在最后默认加上 return 0 的语句。
C 语言从 main 函数开始运行是一个约定俗成的东西,甚至影响了后面的好多语言也是从类似名为 main 函数的入口开始运行程序。但是当内核执行 C 语言程序的时候,并不是直接调用了 main 函数,而是先通过一个特殊的启动程序 (a special start-up routine),这个特殊的启动程序将作为程序的起始地址开始运行,启动程序会从内核中获取命令行参数和环境变量值,然后去调用 main 函数。
一个进程的正常终止是有五种方式:
- 从 main 函数返回
- 调用 exit 函数。
- 调用 _exit 函数或者 _Exit 函数。
- 最后一个线程从其启动例程返回。
- 从最后一个线程调用 pthread_exit 。
所以,这个启动的例程实际上调用的是 exit(main(argc, argv[])) 这个函数。
退出程序有三种分别是
- exit()
- _exit()
- _Exit()
C 语言结束的时候,也是调用的 exit 函数,exit 函数在执行的时候,会先执行一些清理处理,并且关闭所有可能的 IO 流,对所有在缓冲的数据进行冲洗。
如果直接调用 _exit 或者 _Exit 函数,则会直接退出,进入内核。
这三个函数都有一个调用参数,称为终止状态,如果调用这些函数不带这个终止状态,或者 main 函数执行了一个没有返回值的 return 语句(这个时候相当于调用的 exit 函数没有参数),或者 main 函数没有声明返回值为整数类型,那么这个进程终止的状态就是未定义的。否则,进程的终止状态就是 0 。
一个完整的 C 语言的启动过程如图。

我们之前说过,exit 函数退出的时候,还需要进行收尾工作,那么如果我们想自定义这个收尾工作的话,可以调用 atexit () 函数,它会登记我们想要调用的收尾工作调用的函数,然后让 exit 自动进行调用。这些收尾函数可以称为 handler 。
atexit 函数原型如下,它的参数是一个函数地址,exit 调用这些函数的顺序与登记这些函数的顺序相反,可以理解为存在一个handler 函数栈,越后登记的函数越先被调用。
int atexit( void (*func) (void));
C 程序的存储空间布局
一个典型的C语言程序结构如图。

C 程序一般由几个部分组成,
- 正文段/程序段 (text) ,就是记录了程序的二进制代码。通常是只读的,防止出现意外指令被改写。
- 初始化数据段 (Data) 存储已经初始化的全局变量或者静态变量。
- 未初始化数据段(BSS)保存了未初始化的静态变量,在程序家在的时候会将此段中的数据初始化为 0 。
- 栈 保存局部变量,函数调用栈信息,程序开始时自动分配内存,结束后自动释放内存。递归函数调用的时候,会形成一个新的栈帧,函数调用不会互相影响。
- 堆 在堆中进行动态的内存分配,需要手动分配和释放内存。
C语言程序运行时的一些细节的更多相关文章
- Swift和OC,是编译型语言、解释性语言、运行时语言
首先需要明确的一点是,什么是编译型语言和解释性语言 编译型语言,就是在其执行过程中需要先将其经过编译成机器码来给计算机识别的,其执行效率就会比较高这个是显而易见的,常见比如:C.C++ 而解释型语言, ...
- java程序运行时内存分配详解
java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...
- Java程序运行时内存划分
1.Java程序跨平台运行的原因 主要原因是:各种平台的JVM和字节码文件 Java源程序--具体平台的机器代码文件---被编译器翻译成平台无关的Class文件,又用特定JVM运行字节码文件,JVM在 ...
- 小程序运行时如何助力传统APP转型?
小程序和H5或者RN有什么区别?优越性在哪里? 长期以来,移动互联网界一直在寻找一种既能获得Native原生的体验,又可以低门槛快速开发的技术.在这个过程中出现了很多尝试,例如React Native ...
- c/c++编译时,指定程序运行时查找的动态链接库路径
http://blog.csdn.net/tsxw24/article/details/10220735 c/c++编译时,指定程序运行时查找的动态链接库路径 分类: c/c++ linux 2013 ...
- C# 获取程序运行时路径
Ø 前言 开发中,很多时候都需要获取程序运行时路径,比如:反射.文件操作等..NET Framework 已经封装了这些功能,可以很方便的使用. C# 中有很多类都可以获取程序运行时路径,我们没必要 ...
- java程序运行时内存分配详解 (转)
转自:http://www.tuicool.com/articles/uU77v2 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Ja ...
- linux下实现在程序运行时的函数替换(热补丁)
声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术原作者的分享. 但是injso文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的. ...
- 【Visual Studio】控制台程序运行时一闪而过
问题:写一个C#控制台程序,运行时控制台一闪而过.程序结束.无法看清控制台输出的结果. 办法: 方法一二三都有人写过百度经验了:怎么解决VS运行程序一闪而过 其实还有一种办法,就是把控制台程序改为Wi ...
随机推荐
- centOS系统安装-RabbitMq
前言 消息通知机制是我们在日常业务开发总常常都会遇到:在微服务架构里,消息也是必不可少的,我们可以借助它异步实现很多业务,就拿我们日常的购物需求来说,在我们下单支付之后,我们就可以通过消息机制来异步处 ...
- 在vue中使用基于d3为基础的dagre-d3.js搞定一个流程图组件
项目中想搞定一个流程图,开始使用了阿里的G6,但是G6目前不支持手势,这样就很郁闷了,因为公司的领导都是使用iPad看的,你不支持手势是不行的,后来又想到了百度的echarts,试了试,感觉还不错,手 ...
- 在文件夹下所有文件中查找字符串(linux/windows)
在linux下可以用 grep "String" filename.txt#字符串 文件名grep -r "String" /home/#递归查找目录下所有文件 ...
- css实现input表单验证
有没有办法只通过css来确定input标签是否有输入? 我有这个想法是因为我想完成一个自动补全的input部件,最基本的功能是: 如果input没有内容,这隐藏下拉框 反之,显示下拉框 我找到了一个也 ...
- ruby2.2 DevKit 安装后无法使用解决方案
windows 系统下,Ruby 的某些 gem 包需要 DevKit 才能正常安装,2.4 以后的版本可以一键安装 DevKit,之前的版本只能手动安装. 2.4 以后的可以到官网下载:https: ...
- RDS关系型数据库 入门 01 创建关系型数据库实例【华为云分享】
[摘要] 关系型数据库(Relational Database Service,简称RDS)是一种基于云计算平台的即开即用.稳定可靠.弹性伸缩.便捷管理的在线关系型数据库服务.RDS具有完善的性能监控 ...
- 【华为云网络技术分享】HTTP重定向HTTPS配置指南
[摘要] 本文介绍使用华为云弹性负载均衡配置Http重定向到Https的方法. 1. HTTP.HTTPS 头部标识 ELB 对 HTTPS 进行代理,无论是 HTTP 还是 HTTPS 请求,到了 ...
- nginx编译安装配置模块大全
使用configure命令配置构建.它定义了系统的各个方面,包括允许nginx用于连接处理的方法.最后,它会创建一个Makefile.该configure命令支持以下参数:--help 打印帮助信息. ...
- python学习笔记—DataFrame和Series的排序
更多大数据分析.建模等内容请关注公众号<bigdatamodeling> ################################### 排序 ################## ...
- [Input-number]数字输入框组件
需求 加.减按钮 初始值 最大.最小值 数值改变时,触发一个自定义事件来通知父组件 目录文件 index.html 入口页 input-number.js 数字输入框组件 index.js 根实例 实 ...