博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

gcc编译程序四个阶段 预处理、编译、汇编、链接

Posted on 2019-12-06 10:46  bw_0927  阅读(525)  评论(0)    收藏  举报

https://blog.csdn.net/Hanani_Jia/article/details/81735517

 

 

 

 

一般分为四步分别是:预处理、编译、汇编、连接

 

  • 预处理:

预处理阶段的指令一般都是以#来开头的,所以我们编写的程序中像#include、#define等等都是在这一个阶段来完成的

第二个就是预处理阶段会把我们程序中的所有宏进行替换掉

第三个任务是我们在编写程序的时候常常会写入一些注释

第四个就是我们的条件编译我们常常会写#ifdef这时候我们不符合条件的那一部分我们机器也是不会看到的,他不会进入到编译阶段。

 

gcc -E test.c -o test.i

 

  • 编译阶段

当你的程序没有问题的时候编译还会把你的程序编程更接近机器语言的汇编语言。

gcc -S test.i  -o test.S

-S的意思是让我们的文件只进行编译而不进行汇编,生成汇编代码

 

  • 汇编阶段

把我们第二阶段生成的汇编代码变成我们的可执行文件,也就是把我们的汇编语言变成我们的机器可以执行的机器语言,这是每个程序必须经过的阶段

gcc -C test.S -o test.o

-C的意思是让我们的程序执行完第三阶段生成一个机器可以看懂的机器语言

 

 

  • 链接阶段

现在我们的文件机器是可以看懂了,但是还有很多步骤要执行,比如我们当前的源文件引用了另外的头文件中的函数,或者在源文件中调用了某些库中已经写好的函数,

这时候我们执行的时候如果不把他们链接起来都是独立的一块一块的话,程序是不能够正确的执行的,所以我们就要进行第四步连接。

gcc  test.o -o test

 

 

 

这里是几个我们常用的gcc选项。这里我们涉及到了一个新的概念就是函数库的链接,我们在刚刚的程序调用了printf函数,这个函数我们自己并没有实现但是却直接调用了,我们上边也讲到了原因是引用的头文件,但是为什么我们只是简单的写了一个#incldue<stdio.h>就直接把我们的printf函数引用过来了呢?

  原因是这些函数已经被写入到了一个库中,在没有进行特别指定的时候gcc会到某个默认的路径下去寻找这个库,也就是链接到这个库中去把这些文件变成一个整体然后再去执行就没有问题了。函数库一般有两种:静态库和动态库。简单来说静态库就是当我用到这个库的时候,我会直接生硬的把库中的文件添加到你的源文件中,假如我们这个库里边有一百个函数,但是我这里只用了一个printf函数,程序也会把所有的函数都插入到你的程序中,在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。

这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中静态链接库实际上是一个目标文件.o的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

但是动态链接就不一样,动态链接并没有说把库文件的代码插入到可执行文件中,而是在程序执行的时候由链接程序来加载库,这样可以节省大量的系统开销这种库的后缀一般是.so,我们刚刚提到的stdio的名字就是libc.so.6它就是一个动态库。

使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。

但并不是使用动态链接就一定比使用静态链接要优越。

在某些情况下动态链接可能带来一些性能上损害。

静态链接的话代码的装载速度快,执行速度也较快,因为编译时它只会把你需要的那部分链接进去,应用程序相对比较大

。但是如果多个应用程序使用的话,会被装载多次,浪费内存。

这里并没有说确定的界限,所以到底是用动态的还是静态的还是要具体情况具体分析。
————————————————