【Linux】程序的翻译过程

程序的翻译过程分为:预处理、编译、汇编、链接

在Linux中,我们可以用gcc命令的各种选项看到翻译的各过程,可以在每个阶段停下来,并且可以看到中间的翻译结果,这样就更便于我们理解翻译过程

【Linux】程序的翻译过程_第1张图片

【Linux】程序的翻译过程_第2张图片

1.预处理阶段

gcc -E 

gcc -E test.c -o test.i

【Linux】程序的翻译过程_第3张图片

形成一个test.i文件,文件中保存的是gcc -E产生的临时结果

【Linux】程序的翻译过程_第4张图片

头文件展开

我们的test.c文件中只有24行,结果test.i中多出800多行,那多出来的这么多是什么呢?

【Linux】程序的翻译过程_第5张图片

其实这么多代码都是从stdio.h这个头文件展开来的

在预处理阶段,编译器会将我们源代码中所需要的头文件拷贝到源文件中来,我们的头文件中可能也会包含头文件,所以可能会进行递归的拷贝,这个过程叫做头文件展开

【Linux】程序的翻译过程_第6张图片

在安装编译器的时候,C标准库的头文件一般会一并下载到/usr/include/

【Linux】程序的翻译过程_第7张图片

我们可以打开stdio.h看一下

【Linux】程序的翻译过程_第8张图片

对比一下,确实我们展开的是stdio.h

【Linux】程序的翻译过程_第9张图片

我们可以看到预处理阶段进行了宏替换注释也被替换

【Linux】程序的翻译过程_第10张图片

条件编译

我们现在下载的软件,大多都分为好几个版本:专业版、社区版、学生版...

那这是怎么维护的呢,如果一个版本有一份源代码,那维护起来的成本是非常大的,这就可以用条件编译来解决这个问题了,只需要维护一份代码,可以用条件编译进行代码的动态裁剪

我们在C语言阶段就有过条件编译的说明:#ifdef #elif #else #endif

具体在《C语言 预处理》专栏的条件编译有说明

C语言 预处理详解-CSDN博客

【Linux】程序的翻译过程_第11张图片

我们编译成proj.i看一下

【Linux】程序的翻译过程_第12张图片

我们可以看到,满足条件的保留,不满足条件的删除

【Linux】程序的翻译过程_第13张图片

【Linux】程序的翻译过程_第14张图片

gcc -D 动态添加宏

我们可以不在文件中宏定义,可以通过gcc -D进行命令行式的宏定义,这样我们就可以动态地向源代码添加宏

【Linux】程序的翻译过程_第15张图片

2.编译阶段

预处理的结果是test.i,是一份干净的C语言代码

gcc -S

gcc -S test.i -o test.s

gcc -S产生一个test.s的临时结果

【Linux】程序的翻译过程_第16张图片

但是这个代码我们可能看不懂,但是我们知道这是汇编语言

【Linux】程序的翻译过程_第17张图片

3.汇编阶段

gcc -c

gcc -c test.s -o test.o

gcc -c 将test.s文件转成test.o文件,.o表示.obj,在vs中我们编译文件就会产生.obj文件

【Linux】程序的翻译过程_第18张图片

产生的.obj文件叫做目标文件,这个目标文件不能直接执行,最终形成.exe可执行程序才能运行

【Linux】程序的翻译过程_第19张图片

4.链接阶段

gcc test.o -o my.exe

只有最终形成可执行文件,才可以执行

【Linux】程序的翻译过程_第20张图片

关于链接,我们有三个问题:

  • 是什么?
  • 为什么?
  • 怎么办? 

是什么? 

链接的过程是我们的程序和库结合的过程

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用

库:语言一定要有自己的标准库

我们可以用ldd命令来看到对应的动态库

【Linux】程序的翻译过程_第21张图片

这就是我们的C标准库

在安装开发环境的时候,会安装C标准库+C头文件,这时候我们才可以包含对应的头文件,调用头文件里声明的函数

函数库

函数库一般分为静态库和动态库两种

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。

  • Linux中,动态库.so 静态库.a
  • Windows中,动态库.dll 静态库.lib

为什么?

  • 让开发站在巨人的肩膀上
  • 提高开发的效率

怎么办?

链接时,两种链接方式:

  • 动态链接
  • 静态链接

【Linux】程序的翻译过程_第22张图片

动态链接

【Linux】程序的翻译过程_第23张图片

动态库和动态链接的优缺点

  1. 不能丢失
  2. 节省资源 

静态链接

【Linux】程序的翻译过程_第24张图片

静态库和静态链接的优缺点

  1. 一旦形成,和库无关
  2. 浪费资源 

C动态库,是默认提供的

gcc默认形成的可执行程序,默认采用动态链接

而Linux中静态库默认是没有的

安装静态库我们可以用yum指令

sudo yum install -y glibc-static libstdc++-static

【Linux】程序的翻译过程_第25张图片

这时我们就可以编译通过了

【Linux】程序的翻译过程_第26张图片

【Linux】程序的翻译过程_第27张图片

同样运行也能通过

静态链接的应用场景

由于静态链接不依赖于任何的动态库,所以在移植到其他环境中时就不需要做过多的环境检测,可以直接运行,方便部署

你可能感兴趣的:(#,Linux,linux,运维,服务器)