【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.cadd.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打开来观察一下

预编译结果:

预编译结果其实完成了这么几件事情:

编译

指令:-S
结果:生成一个.i文件,里面是汇编代码
所以在这时期:把C语言代码翻译成了汇编代码

  1. 语法词法分析
  2. 语义分析
  3. 符号汇总(把一些全局的符号汇总起来,为下一步汇编时符号表的生成做准备

汇编

指令: -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(编译器)的使命就完成了。
那么符号表到底有啥用呢?接下来,到了链接环节。

链接

链接

  1. 合并段表
  2. 符号表的合并和重定位

合并段表:就是把相同的东西合并在一起。 这个过程博主不详细给大家讲解了。
接下来我们来详细说说,符号表的合并和重定位。

我们来看看上一阶段我们生成的两个符号表:

我们要将其合并。
合并的意思就是,我们来找相同的符号。
比如Add符号,链接器找到两个Add。这时,链接器会发现,有一个Add的地址是有效的,有一个是无效的,此时链接器会丢弃无效的那个地址,保留有效的地址。

其实简单来说就是把源文件弄在一起了。

那么我们懂得这个知识之后,我们就可以知道我们平时在学习过程中常见的错误是什么意思了。

  • 如果我们将刚才的add.c里面的内容删掉,或者把Add函数改个名字,这样我们在合并符号表的时候,test.c里面那个Add是无效的,这时,链接器就会去表里找,找有效的Add,找不到,就会报错了,这个错误就是链接错误!



这样我们就能很好地理解这个错误是怎么报出来的了。
成功生成目标文件之后,链接期间,找不到Add了—报链接错误。

运行环境

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序
    的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用main函数。

尾声

如果对预编译(预处理)这个过程感兴趣的伙伴,可以通过博主提供 的传送门食用。【预定义】C语言预定义代码(宏、条件编译等)内容介绍【最全的保姆级别教程】
看到这里,相信大家已经对程序的编译,运行等过程有了比较深入的了解了,其实学习这些对我们写代码能力的帮助其实是不大的,但是这些都是内功的修炼,都是硬核知识,在我们找工作,或者在完成项目时,这种深入的理解对我们的帮助是非常大的。
如果你觉得这篇文章对你有帮助的话,不要忘了一键三连后再离开噢!

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

  1. C语言深度解剖读书笔记

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  2. C语言深度解剖读书笔记(6.函数的核心)

    对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求 ...

  3. 【转】 C语言深度解剖读书笔记(1.关键字的秘密)

    本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...

  4. java程序运行时内存分配详解

    java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下   一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...

  5. Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...

  6. Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...

  7. Java程序在内存中运行详解

    目录 Java程序在内存中运行详解 一.JVM的内存分布 二.程序执行的过程 三.只有一个对象时的内存图 四.两个对象使用同一个方法的内存图 五.两个引用指向同一个对象的内存图 六.使用对象类型作为方 ...

  8. Jmeter(三十九) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 上篇(详解教程)

    ------------------------------------------------------------------- 转载自:北京-宏哥 https://www.cnblogs.co ...

  9. Myeclipse程序调试快捷键及步骤详解

    Myeclipse程序调试快捷键及步骤详解: 调试快捷键    Eclipse中有如下一些和运行调试相关的快捷键.    1. [Ctrl+Shift+B]:在当前行设置断点或取消设置的断点.    ...

  10. Go语言备忘录(2):反射的原理与使用详解

    本文内容是本人对Go语言的反射原理与使用的备忘录,记录了关键的相关知识点,以供翻查. 文中如有错误的地方请大家指出,以免误导!转摘本文也请注明出处:Go语言备忘录(2):反射的原理与使用详解,多谢! ...

随机推荐

  1. AtCoder Beginner Contest 210 (A~E)

    比赛链接:Here A - Cabbages 略 B - Bouzu Mekuri 略 C - Colorful Candies 用map维护连续一段区间的不同元素即可. int main() { c ...

  2. VScode 中 Code Runner 插件乱码问题

    安装好,Code Runner 插件,可以很方便的运行多种语言的文本,但是提示和输出经常会报错 进入 File - > Preference -> setting, 然后在输入框搜索 se ...

  3. java实现mysqlplus查询一个月之间的数据

    先说需求使用mysqlplus查询一个月之内的数据,传入的参数是202108 要求就查8月份这个月的所有数据,oracle数据中数据记录的时间类型是Date类型 public static void ...

  4. 使用 FormatMessage 格式化 Windows 错误码.md

    https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage #include <str ...

  5. java基础-java面向对象-02-day09

    目录 1. 封装 2. 继承 2.1 什么是方法的重写 2.2 super 2.3 object详解 2.4 equals方法 3. 多态 4. final修饰符 5.抽象类 6. 接口 7. 内部类 ...

  6. APB Slave设计

    APB Slave位置 实现通过CPU对于APB Slave读写模块进行读写操作 规格说明 不支持反压,即它反馈给APB的pready信号始终为1 不支持错误传输,就是说他反馈给APB总线的PSLVE ...

  7. Error adding module to project: null I(IDEA创建maven项目时遇到此提示)

    1.问题 在已创建一个springboot项目的时候,想要再增添一个Maven项目,发生报错 2.解决 根本原因是创建maven模块时选择的sdk版本过高(选用了openjdk19).选用jdk1.8 ...

  8. 【Git】如何在github上提交PR(Pull Request)

    [来源]https://mp.weixin.qq.com/s/yHQRjpVeZVV4PuoUKM0FSw

  9. Qt5.9 UI设计(三)——添加UI、类及资源文件

    前言 设计一个软件,最简单的方式就是把控件直接往UI上放,然后再把功能实现了.这样可以实现基本的功能,但是界面不能缩放,如果拖动软件改变界面的大小,界面上的控件就会乱成一团,或者是界面的控件压根就不会 ...

  10. Java21 + SpringBoot3整合Redis,使用Lettuce连接池,推荐连接池参数配置,封装Redis操作

    目录 前言 相关技术简介 Redis 实现步骤 引入maven依赖 修改配置文件 定义Redis配置类 定义Redis服务类,封装Redis常用操作 使用Redis服务类 总结 前言 近日心血来潮想做 ...