Linux下Gcc编译

来源:互联网 发布:身份证照片查询软件 编辑:程序博客网 时间:2024/05/22 02:15

gcc编译流程:


a.预处理(Pre-processing)
读取C语言源文件,对以“#”开头的语句进行处理
将头文件展开:  将头文件的内容展开至C文件
做宏的替换:  将源文件中出现的宏做宏值替换。
条件编译的判断 : 根据条件编译选项,加载指定代码
(注:该阶段会删除注释代码)
可以通过gcc的选项-E来单独执行预处理,而不处理其它三个步骤,并生成gcc-test.i的文件:
gcc -E gcc-test.c -o gcc-test.i

在预处理时,gcc编译器仅仅做的只是对头文件的展开、宏的替换、条件编译选项的判断,注释删除操作(注:不做语法检测)。
因此,在预处理时,gcc编译器不会出现任何错误提示。即便是非C源代码的普通文本文档,我们也可以使用预处理执行。

b.编译(Compilation)
在这个阶段中,gcc将对所有正文内容进行处理
对预处理之后输出的文件进行词法分析,试图找出所有不符合语法规则的部份,打印错误或警告。若没有任何语法问题,则将其翻译为功能等价的汇编代码

编译成功后,使用cat 指令查看gcc-test.s的文件信息:

编译总结:
找出源文件中的语法的错误,没有错误生成汇编文件.   因此基本上的错误显示都是在该步骤出现

c.汇编(Assembly)
在这个阶段中,gcc将对汇编文件进行处理:
将编译阶段生成的.s汇编文件翻译成二进制机器代码。
可以通过gcc的选项-c来执行汇编,并生成gcc-test.o的文件:

总结:
将高级的语言代码翻译成机器语言代码。
思考题:将以下代码汇编成机器语言代码,是否会出现错误?


d.链接(Linking)
在这个阶段中,若之前三个步骤没有任何错误的情况下,gcc将最后生成我们的可执行程序:
链接没有任何的选项。

将一个文件中引用的符号(函数、外部变量等)与该符号在另文件中的定义和实现连接起来,从而使有关的目标文件连成一个整体,最终生成可以被操作系统执行的可执行文件
在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的<stdio.h>中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?
最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,gcc会到系统默认的搜索路径”/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf” 了,而这也就是链接的作用

链接总结:
链接生成最后可执行程序
链接做符号(函数)的引用和链接。
若函数找不到实现,将会有链接错误提示。
通过ldd命令可以查看某个可执行文件的库链接状态


gcc的基本选项:

-g :  在可执行文件中加入调试信息,方便进行程序的调试,方便使用gdb来进行调试
比较加选项g和不加g的可执行程序大小区别:
-O[0、1、2、3] :对生成的代码使用优化,中括号中的部分为优化级别,缺省的情况为2级优化,0为不进行优化。
注意,采用更高级的优化并不一定得到效率更高的代码。
-D :宏定义
 一般格式-Dname=value,若vlaue为空缺省为1
在C程序源文件中,可以使用#define来定义一个宏
在编译时,我们也可以使用gcc来定义一个宏,与用#define定义的效果是一样的


-Idir :在编译源程序时增加一个搜索头文件的额外目录dir,即include增加一个搜索的额外目录。
linux系统下:默认的头文件存放路径
/usr/include/   /usr/local/include/
例:
增加一个额外的头文件搜索路径,当前目录下的./include


-Ldir :在编译源文件时增加一个搜索库文件的额外目录dir
LINUX下:默认的库搜索路径为/lib/ 、 /usr/lib/

-llibrary :在编译链接文件时增加一个额外的库,类似于源文件中使用#include增加头文件一样
库名的命名标准为lib + 库名 + 库类型

函数库

在windows平台和linux平台下都大量存在着库。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。 由于windows和linux的本质不同,因此二者库的二进制是不兼容的。
linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。
静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。“空间换时间”
共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。 “时间换空间”

库是别人写好的现有的,成熟的,可以复用的。
现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
库实例:
写三个文件fun1.c  fun2.c   fun3.c 
这三个文件中分别对应有函数fun1(), fun2(), fun3(),现在希望将这三个文件分别编译成静态库与动态库。
写一个文件main.c 在main.c中有main函数,并调用fun1(), fun2(), fun3()函数。

静态库生成:
生成源文件的目标机器文件

对目标机器文件进行归档

编译可执行程序并加载静态库,执行main程序,看其效果静态库加载到可执行程序中,只需在编译的时候,跟上需要加载的库名即可

动态库 (共享库)编译与加载
生成源文件的目标机器文件

对目标机器文件进行处理,编译出动态库

编译可执行程序并加载静态库,执行main程序,看其效果
但是需要将库所在路径添加到环境变量中,或者将此库放到对应库路径里
LINUX下:默认的库加载路径为/lib/ 、 /usr/lib/










0 0
原创粉丝点击