gcc ------->默认编译c文件(gcc xxx -lstdc++链接到c++库编译c++,g++默认编译c++)
一.CC编译程序过程分四个阶段
◆ 预处理(Pre-Processing)
◆ 编译(Compiling)
◆ 汇编(Assembling)
◆ 链接(Linking)
1.1 预处理(Pre-Processing)
gcc -E test.c -o test.i 或 gcc -E test.c
可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码.
gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。预处理结果就是将#include等类似的文件中的内容插入到当前文件中。
1.2 编译(Compiling)
预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc -S test.i -o test.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
1.3 汇编(Assembling)
对于生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
gcc -c test.s -o test.o
1.4 链接(Linking)
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
gcc test.o -o test
这个程序,一步到位的编译指令是:
gcc test.c -o test
二:库文件链接
开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so、或lib、dll)的集合。。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下;Windows所使用的库文件主要放在Visual Stido的目录下的include和lib,以及系统文件夹下。但也有的时候,我们要用的库不再这些目录下,所以GCC在编译时必须用自己的办法来查找所需要的头文件和库文件。(头文件的路径用-I,库文件路径用-L,比如说-I/usr/myinclude -L/usr/mylib)
-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了(把函数库命名为“lib“是UNIX的惯例,“.so”是共享文件库的标准扩展名)
2.1 编译成可执行文件
首先我们要进行编译test.c为目标文件,这个时候需要执行
gcc –c –I /usr/dev/mysql/include test.c –o test.o
2.2 链接
最后我们把所有目标文件链接成可执行文件:
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
Linux下的库文件分为两大类分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),二者的区别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
2.3 强制链接时使用静态链接库
默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usr/dev/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test
静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
总结:源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码
三:makefile工具
make是一个命令工具,是一个解释makefile中指令的命令工具,make会在当前目录下找名字叫“Makefile”或“makefile”的文件。make会一层又一层地去找文件的依赖关系(如果该过程文件比生成的目标文件新,需重新编译),直到最终编译出第一个目标文件。
Makefile的规则
target ... : prerequisites ...
command
...
...
target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
3.1 基础makefile
target : prerequisites1.o prerequisites2.o prerequisites3.o / cc -o target prerequisites1.o prerequisites2.o prerequisites3.o display.o prerequisites1.o : prerequisites1.c prerequisites1.h cc -c main.c prerequisites2.o : prerequisites1.c prerequisites2.h cc -c prerequisites2.c prerequisites3.o : prerequisites3.c prerequisites3.h cc -c prerequisites3.c clean : rm target prerequisites1.o prerequisites2.o prerequisites3.o
反斜杠(/)是换行符的意思。在这个makefile中,目标文件(target)包含:中间目标文件(*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 target 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的。
在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。
3.2 变量makefile
上例看到编译目标文件时[.o]文件的字符串被重复了两次,如果我们的工程需要加入一个新的[.o]文件,那么我们需要在两个地方加(应该是三个地方,还有一个地方在clean中)。我们声明一个变量OBJ等于依赖的[.o]文件,然后在我们的makefile中以“$(objects)”的方式来使用这个变量
3.3 make自动推导
GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令。make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个prerequisite1.o,那么prerequisite1.c,就会是prerequisite1.o的依赖文件。并且 cc -c prerequisite1.c 也会被推导出来。如下代码:
OBJ = prerequisites1.o prerequisites2.o prerequisites3.o target : $(OBJ)/ cc -o target $(OBJ) prerequisites1.o : prerequisites1.h prerequisites2.o : prerequisites2.h prerequisites3.o : prerequisites3.h .PHONY : clean clean : rm target $(OBJ)
这种方法,也就是make的“隐晦规则”。上面文件内容中,“.PHONY”表示,clean是个伪目标文件。