【C语言深度解剖】一篇解决程序的环境【编译+链接详解】让面试官给我们竖起大拇指

【C语言深度解剖】【Linux操作系统】程序的环境【编译+链接详解】
那么这里博主先安利一下一些干货满满的专栏啦!
作者: #西城s
这是我的主页:#西城s
在食用这篇博客之前,博主在这里介绍一下其它高质量的编程学习栏目:
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!
力扣刷题专栏:Leetcode想要冲击ACM、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!
C的深度解剖专栏:C语言的深度解剖想要深度学习C语言里面所蕴含的各种智慧,各种功能的底层实现的初学者们,相信这个专栏对你们会有帮助的!
今天博主将带大家详细深入地解剖C程序编译的过程,这些是我们内功修炼的必要知识,学会这些,让面试官给你竖起大拇指。
不收藏必后悔系列篇~

程序的翻译环境
- 在ANSI C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令(.exe)。
第2种是执行环境,它用于实际执行代码。
即:

翻译环境详解
首先,我们知道编译这个过程,可以将test.c变成test.exe,那么,在此期间的过程,我们也要深入研究。
翻译环境基本原理:
每个源文件单独经过编译器处理后,生成相对应的目标文件(.obj)之后,结合链接库,经过编译器处理后,形成可执行程序(.exe)。
IDE介绍:
VS2022
DEV C++
CodeBlocks
Clion
集成开发环境-IDE
继承了编辑,编译,链接,调试等功能
这些IDE里面在下载的时候都一定带有两个可执行程序
cl.exe(编译器),link.exe(编译器)
具体过程如下:
如图:

test.c----test.obj
contact.c-----contact.obj
每个源文件经过编译器生成对应的目标文件(.obj)文件之后
加上链接库经过链接器—可执行程序(链接库:库函数等)
编译
编译:预处理+编译+汇编
经过上述阶段之后,我们就可以得到.obj文件了。
讲解代码:
我们创建两个源文件test.c和add.c
test.c:
extern int Add(int, int);
int main() {
int a = 10;
int b = 20;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
add.c:
int Add(int x, int y) {
return x + y;
}
我们就用这两个简单的代码给大家解释编译的详细过程,以及预处理,编译,汇编的每一步的结果
- 由于
VS等IDE软件已经高度集成,我们无法看到编译中间的详细过程,因此博主在这里使用linux操作系统下的gcc编译器为大家演示。
我们先写好代码:
使用vim编辑器编辑源文件:



写好代码,我们先跑一次,看看有没有问题出现先,后面我们再拆开每一步讲解:

我们可以看到,结果是30。说明没问题,ok,我们开始拆解编译过程。
预编译
为了更好的观察每一个步骤,这里食用Linux gcc来演示
gcc test.c -E (在预编译之后停下来)
再-o 放到test.i里面
所以
gcc test.c -E -o test.i
用vim打开来观察一下
预编译结果:

预编译结果其实完成了这么几件事情:
- 头文件的包含 我们刚才打开
test.i看到前面的一大堆,其实都是头文件的包含,就是把头文件内容复制进去了。 - 预定义指令实现的替换和删除 #define所定义的符号完成替换,宏完成替换,条件编译(为假)的内容完成删除。如果对C语言预定义问题还存在疑问的伙伴,可以通过博主的传送门学习,也是满满干货的噢!【预定义】C语言预定义代码(宏、条件编译等)内容介绍【最全的保姆级别教程】
- 注释完成删除
编译
指令:-S
结果:生成一个.i文件,里面是汇编代码。
所以在这时期:把C语言代码翻译成了汇编代码
- 语法词法分析
- 语义分析
- 符号汇总(把一些全局的符号汇总起来,为下一步汇编时符号表的生成做准备)


汇编
指令: -c
结果:生成test.o文件
windows环境下:目标文件:xxx.obj
linux环境下:目标文件:xxx.o
目标文件是二进制的—是看不懂的
此过程:
- 把汇编指令翻译成二进制指令
完成的东西:
- 形成符号表


关于形成符号表
在汇编这个过程,符号表的形成时需要我们理解的,这是在为链接做准备。
形成符号表是啥意思呢?
- 简单来说就是让全局的符号形成一个符号和地址的表:比如
Add,因为这个符号是要跨文件使用的,test.c里要用,add.c里也要用,这些符号是要形成符号表的,但是add.c里面的临时变量x,y这些是不用的,因为它们只在add.c里面的一个函数里能用。
比如:
test.o的符号表
| 符号 | 地址 |
|---|---|
| Add | 0x00 |
| main | 0x19 |
add.o的符号表
| 符号 | 地址 |
|---|---|
| Add | 0xff |
注意:
- 我们
test.o所对应的Add符号的地址,是无效地址,为什么:因为在test.c里面我们根本没有定义Add这个函数所以无效,而add.o里面那个才是有效的。
那么我们如何去演示呢?
首先.o文件我们肯定是看不懂的,因为这个文件的格式其实是叫做elf格式,Linux上有一个工具叫做readelf可以帮我们翻译这个.o文件。

里面有很多选项可以看,我们看符号表就行了


对比两个符号表:

形成符号表之后,我们编译这个环节,就完成了,cl.exe(编译器)的使命就完成了。
那么符号表到底有啥用呢?接下来,到了链接环节。
链接
链接:
- 合并段表
- 符号表的合并和重定位
合并段表:就是把相同的东西合并在一起。 这个过程博主不详细给大家讲解了。
接下来我们来详细说说,符号表的合并和重定位。
我们来看看上一阶段我们生成的两个符号表:

我们要将其合并。
合并的意思就是,我们来找相同的符号。
比如Add符号,链接器找到两个Add。这时,链接器会发现,有一个Add的地址是有效的,有一个是无效的,此时链接器会丢弃无效的那个地址,保留有效的地址。
其实简单来说就是把源文件弄在一起了。
那么我们懂得这个知识之后,我们就可以知道我们平时在学习过程中常见的错误是什么意思了。
- 如果我们将刚才的
add.c里面的内容删掉,或者把Add函数改个名字,这样我们在合并符号表的时候,test.c里面那个Add是无效的,这时,链接器就会去表里找,找有效的Add,找不到,就会报错了,这个错误就是链接错误!


这样我们就能很好地理解这个错误是怎么报出来的了。
成功生成目标文件之后,链接期间,找不到Add了—报链接错误。
运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。 - 程序的执行便开始。接着便调用
main函数。
尾声
如果对预编译(预处理)这个过程感兴趣的伙伴,可以通过博主提供 的传送门食用。【预定义】C语言预定义代码(宏、条件编译等)内容介绍【最全的保姆级别教程】
看到这里,相信大家已经对程序的编译,运行等过程有了比较深入的了解了,其实学习这些对我们写代码能力的帮助其实是不大的,但是这些都是内功的修炼,都是硬核知识,在我们找工作,或者在完成项目时,这种深入的理解对我们的帮助是非常大的。
如果你觉得这篇文章对你有帮助的话,不要忘了一键三连后再离开噢!
【C语言深度解剖】一篇解决程序的环境【编译+链接详解】让面试官给我们竖起大拇指的更多相关文章
- C语言深度解剖读书笔记
开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...
- C语言深度解剖读书笔记(6.函数的核心)
对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++); 这样的问题大多跟编译器有关,不要去刻意追求 ...
- 【转】 C语言深度解剖读书笔记(1.关键字的秘密)
本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...
- java程序运行时内存分配详解
java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...
- Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Java程序在内存中运行详解
目录 Java程序在内存中运行详解 一.JVM的内存分布 二.程序执行的过程 三.只有一个对象时的内存图 四.两个对象使用同一个方法的内存图 五.两个引用指向同一个对象的内存图 六.使用对象类型作为方 ...
- Jmeter(三十九) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 上篇(详解教程)
------------------------------------------------------------------- 转载自:北京-宏哥 https://www.cnblogs.co ...
- Myeclipse程序调试快捷键及步骤详解
Myeclipse程序调试快捷键及步骤详解: 调试快捷键 Eclipse中有如下一些和运行调试相关的快捷键. 1. [Ctrl+Shift+B]:在当前行设置断点或取消设置的断点. ...
- Go语言备忘录(2):反射的原理与使用详解
本文内容是本人对Go语言的反射原理与使用的备忘录,记录了关键的相关知识点,以供翻查. 文中如有错误的地方请大家指出,以免误导!转摘本文也请注明出处:Go语言备忘录(2):反射的原理与使用详解,多谢! ...
随机推荐
- Codeforces Round #741 (Div. 2) 个人题解 A~D
比赛链接:Here 1562A. The Miracle and the Sleeper 题意: 给出 \(l,r\) 求出最大化的 \(a\ mod\ b\) (\(l\le b\le b\le a ...
- P1525 关押罪犯 (并查集 / 二分图)| 二分图伪码
原题链接:https://www.luogu.com.cn/problem/P1525 题目概括: 给你m对关系,每对关系分别涉及到x,y两人,矛盾值为w 请你判断分配x和y到两个集合中,能否避免冲突 ...
- <vue 路由 4、嵌套路由>
一.效果 点击about后,新闻和体育属于about的子路由调用的页面 知识点说明 路由里使用children属性可以实现路由的嵌套 三.代码结构 注:主要是标红的几个文件 四.代码 重新编写这几个文 ...
- 如何使用ps抠图(两种扣图方法)
PS抠图的方法有很多种,以下是其中两种常用的方法: 方法一:适用于背景颜色单一.较为简单的图片. 选中魔棒工具/魔术橡皮擦,点击背景,出现选区,点击Delete键删除. 点击快速选择工具,沿着所需图形 ...
- [转帖]一文说清 Linux System Load
https://zhuanlan.zhihu.com/p/447661302 双十一压测过程中,常见的问题之一就是load 飙高,通常这个时候业务上都有受影响,比如服务rt飙高,比如机器无法登录,比如 ...
- [转帖]Shell脚本数组(实现冒泡排序,直接选择排序,反转排序)
目录 数组 数组定义方法 数组包括的数据类型 获取数组长度 读取某下标赋值 数组遍历 数组切片 数组替换 删除数组 追加数组中的元素 从函数返回数组 加法传参运算 乘法传参运算 数组排序算法 冒泡排序 ...
- [转帖]使用S3F3在Linux实例上挂载Bucket
https://docs.jdcloud.com/cn/object-storage-service/s3fs S3F3是基于FUSE的文件系统,允许Linux 挂载Bucket在本地文件系统,S3f ...
- [转帖]CTF -bugku-misc(持续更新直到全部刷完)
CTF -bugku-misc(持续更新直到全部刷完) https://www.cnblogs.com/cat47/p/11432475.html 1.签到题 点开可见.(这题就不浪费键盘了) CTF ...
- 关于cockpit的学习
关于cockpit的学习 背景 使用node-exporter 可以监控很多资源使用情况 但是这个需要搭建一套prometheus和grafana的工具 并且每个机器都需要安装一套node-expor ...
- [转帖]AnolisOS8安装ntp同步时间
https://www.wlnmp.com/post-673.html 在AnolisOS8中默认不再支持ntp软件包,时间同步将由chrony来实现,如果你习惯了使用ntp来同步时间,一时难以去适应 ...
