【跟着操作就行了】手把手教你 编译+链接 程序环境教程

  

  • 博客内容:编译+链接 程序环境教程

  • 作  者:陈大大陈

  • 个人简介:一个正在努力学技术的准前段,专注基础和实战分享 ,欢迎私信!

  • 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信

目录

 程序的翻译环境和执行环境

详解编译+链接

 翻译环境 

  编译本身也分为几个阶段

预编译(预处理)

编译

汇编

链接

什么是符号汇总?

合并段表

符号表的合并和重定位

 程序的翻译环境和执行环境

ANSI C的任何一种实现中,存在着两种不同的环境

也就是说,我们写出C语言的代码想编译成可以运行的程序,就必须要经过这两种环境。

第一种是翻译环境,在这一环境中源代码被翻译成可执行的机器指令。

另一种是编译环境,用来实际执行代码。

翻译成图大概就是这个样子。 

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第1张图片 其中,test.exe当然就是可执行的二进制指令,也被称为上面所说的机器指令。


详解编译+链接

 翻译环境 

 我们知道,一个程序可以有许多源文件。编译器可以将这众多的源文件转换为目标代码。

而要把这众多的目标文件链接到一起,就需要链接器(linker)来大展身手了!

链接器可以将众多的目标文件捆绑到一起,形成一个单一而完整的可执行程序。

我们可以搜索到编译器和链接器。

 图一是编译器,图二是链接器。

链接器同时也会引入标准C函数库中被该程序所用到的函数,它还可以搜索程序员个人 的程序库,并且将其需要的函数也链接到程序中。

 

我们想观察到编译和链接的过程,就需要用到gcc编译器,为什么不能用VS呢?

因为VS是一个集成开发环境,集成很多功能,一个ctrl+f5直接出结果了,不方便观察编译和链接细节。

  编译本身也分为几个阶段

预编译(预处理)

翻译环境要经过编译和链接两个阶段,编译的过程又分为预编译,编译和汇编。

画成图的话就是下面这样(我 的 肝 好 累)(;´༎ຶД༎ຶ`)

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第2张图片

 我们先在gcc编译器搭建好环境。【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第3张图片

 我们想让test.c文件只是预编译,而不执行后面的操作,那就要这么写命令行。

gcc  test.c -E -o test.i

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第4张图片 这样我们就将预编译的内容写到了一个叫test.i的文件里面。 

 只写gcc test.c -E -o的话,上面那一长串代码就会打印到终端上面。

我们在这一串代码中发现了老朋友头文件stdio.h的身影。

那就引出了预编译的第一个功能——头文件的包含。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第5张图片

 上面是test.i文件里存放的预处理之后的代码。我们发现,SZ被替换成了10,也是它被定义的值。

我们就发现了预编译的第二个功能——#define所定义的符号的替换。

预编译的第三个功能就直接写出了——注释的删除

上面的三个文本操作确实会让代码的行数减少很多。

编译

要观察编译的过程,我们写出这样的命令行。

 运行命令,发现跳出一个叫test.s的文件。

这个文件里的内容和汇编指令十分相似。【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第6张图片

 所以编译阶段所做的事情就是把C语言代码翻译成汇编代码。

当然这之间的事情十分的复杂,要经过语法分析,词法分析,语义分析,符号汇总的操作。

大家可以去购买相关的书籍查阅,像《程序员的自我修养》里面就有相关的解析。

其中的符号汇总我下面会讲。

汇编

我们接着写出这样的命令行  gcc test.s -c。

 

 弹出一个 test.o 文件。

在VS编译器上面,目标文件的后缀是.obj,而在gcc编译器,目标文件的后缀是.o。

所以test.o这个文件就是目标文件了。

目标文件是什么格式呢。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第7张图片

 我们点开这个文件,发现它是二进制文件。

我们就明白了,汇编所做的事情就是将编译出来的汇编代码转换成二进制指令。

链接

 链接过程就是将众多上面的test.o文件编译成test.exe,也就是可执行程序。

输入gcc test.o来直接编译test.o文件。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第8张图片

 生成一个a.exe文件。

直接运行a.exe文件。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第9张图片

同样可以达到test.exe的效果。 

上面说了, 链接过程就是将众多上面的test.o文件编译成test.exe,也就是可执行程序。

那如何编译呢?要经过两个过程:

1.合并段表

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

我就以下面的代码为例来讲解。 

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第10张图片

我们已经知道,上面的代码经过汇编会产生两个 .o 文件,我们就假设一个为Sub.o,另一个为test.o。

什么是符号汇总?

在编译阶段,程序会进行符号汇总的操作。

将函数声明和全局变量等进行汇总,其实就是收集起来。如图所示。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第11张图片

 当完成符号汇总操作之后,程序就该进入汇编阶段了。

汇编阶段就是将编译出来的汇编代码转换成二进制指令。

这里面就包含了形成符号表的操作。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第12张图片

 我们给收集来的符号假定几个地址,其中test.o里面的Sub给上空指针。

接下来就该合并段表和符号表的合并和重定位了。

合并段表

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第13张图片

 各目标文件需要链接库来形成可执行程序。

目标文件,已经是二进制的文件。它们是有格式的。

就以gcc编译产生的目标文件为例,目标文件的格式是elf的文件格式。

可支持程序文件的格式也是elf的。

这种文件格式会把文件分成段。

编译器就把这对应段的文件合并在一起,形成可执行程序。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第14张图片

 当然,这一块的操作是非常复杂的。

符号表的合并和重定位

 Sub.o文件和test.o文件各有各的符号表,难道生成的文件里也是各有各的文件表吗?

当然不是,它们要进行符号表的合并和重定位操作。

 【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第15张图片

 无效的地址会被清除,从而形成一个新的符号表。

它有什么用呢?

当我们删除声明的Sub函数,读取的符号表会变成main函数和空指针的Sub函数声明

我们通过这个空指针函数声明去找未定义的Sub函数时,就会报出链接时错误——无法解析的外部符号。

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第16张图片

【跟着操作就行了】手把手教你 编译+链接 程序环境教程_第17张图片 如果函数的名字写错也是同理。


总结
  感谢观看,本文到这里就结束了,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。

 

  也欢迎你,关注我。

  原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!! 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。

你可能感兴趣的:(C语言初阶以及进阶内容专栏,c++,开发语言,数据结构,c语言,算法)