简介
GCC是GNU Compiler Collection也即GNU编译器家族的缩写。
简单编译
//main.c
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
有上述源文件main.c,在其同目录下执行命令:
gcc main.c -o main
即可在同目录下得到名为main的可执行性文件.(不同的平台有不同的后缀).实际上,上述编译过程是分为四个阶段进行的,即:
- 预处理:Preprocessing
- 编译:Compilation
- 汇编:Assembly
- 连接:Linking
预处理
gcc -E main.c -o main.i
可以输出main.i文件,其中存储着main.c经预处理之后的代码.gcc的-E选项,可以让编译器在预处理后停止,并输出预处理的结果.在本例中,预处理结果就是将stdio.h文件中的内容插到main.c中了.
编译为汇编代码
预处理之后,可直接对生成的main.i文件编译,生成汇编代码:
gcc -S main.i -o main.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后停止,-o输出汇编代码文件.
汇编
对于汇编代码文件main.s,gas汇编器可以将其编译为目标文件:
gcc -c main.s -o main.o
gcc的-c选项,表示将汇编代码编译为目标文件.
连接
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件.附加的目标文件包括静态连接库和动态连接库.对于上述示例中的main.o,可以将其与C标准库进行连接,最终生成可执行性文件.
gcc main.o -o main
多文件编译
通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这边编译单元.假设有一个由test1.c和test2.c两个源文件组成地程序,为了对他们进行编译,并最终生成可执行程序test,可以使用下面这条命令:
gcc test1.c test2.c -o test
如果同时处理的文件不止一个,GCC仍然会按照预处理,编译和连接的过程依次进行.
检错
gcc -pedantic main.c -o main
-pedantic编译选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部.
gcc -Wall main.c -o main
-Wall编译选项能够产生警告信息.
gcc -Werror main.c -o main
-Werror也会产生警告信息,并且会在产生警告的地方停止编译.
GCC编译选项
1) -c :指编译,不链接,生成目标文件“.o”。
2) -S :只编译,不汇编,生成汇编代码“.S”。
3) -E :只进行预编译/预处理,不做其他处理。
4) -o file:把输出文件输出到file里。
5) -g :在可执行程序中包含标准调试信息。
6) -v :打印出编译器内部编译各过程的命令行信息和编译器的版本。
7) -I dir :在头文件的搜索路径列表中添加dir目录
8) -L dir :在库文件的搜索路径列表中添加dir目录
9) -static :连接静态库(静态库也可以用动态库链接方式链接)
10) -llibrary :连接名为library的库文件(显示指定需要链接的动态库文件)
11)-On :gcc可以对代码进行优化,它通过编译选项On
来控制优化代码的生成,n是一个代表优化级别的整数,比较典型的范围为0变化到2或者3。
1) -ansi :支持符合ANSI标准的C程序
2) -pedantic :允许发出ANSI C标准所列出的全部警告信息
3) -pedantic-error :允许发出ANSI C标准所列出的全部错误信息
4) -w :关闭所有警告
5) -Wall :允许发出gcc提供的所有有用的报警信息
6) -werror :把所有的告警信息转化为错误信息,并在告警发生时终止编译过程
GCC生成动态库和静态库
- 多个源文件/目标文件生成动态库
动态库在调用的时候,注意设置export LD_LIBRARY_PATH=$(pwd)
。
gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so
gcc -fPIC -shared xxx1.o xxx2.o xxx3.o -o libxxx.so
- 多个目标文件生成静态库
静态库只能由目标文件打包生成,不能由源文件直接得到。
ar -rc libxxx.a xxx1.o xxx2.o xxx3.o
库文件连接
开发软件时,完全不适用第三方函数库的情况是比较少的,通常来讲都是需要借助许多函数库的支持才能够完成相应的功能.从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so lib dll)的集合.
Linux下的大多数函数都默认将头文件放到/user/include/目录下,而库文件则放到/user/lib/目录下.Windows所使用的库文件主要放在Visual Stido的目录下的include和lib,以及系统文件夹下.
但有的时候,我们要用的库不在这些目录下,所以GCC在编译的时候必须用自己的办法来查找所需要的头文件和库文件.
例如我们的程序test.c是在linux上使用c连接mysql,这个时候我们需要去mysql官网下载MySQL Connectors的C库,下载下来解压之后,有一个include文件夹,里面包含mysql connectors的头文件,还有一个lib文件夹,里面包含二进制so文件libmysqlclient.so.其中inclulde文件夹的路径是/usr/dev/mysql/include,lib文件夹是/usr/dev/mysql/lib。
静态库和动态库的比较
- 静态库:静态库是在链接阶段被链接的,所以生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。
- 动态库:动态库的链接是在程序执行的时候被链接的。所以,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。
链接静态库从某种意义上来说也是一种粘贴复制,只不过它操作的对象是目标代码而不是源码而已。因为静态库被链接后库就直接嵌入可执行文件中了,这样就带来了两个问题:
系统空间被浪费:如果多个程序链接了同一个库,则每一个生成的可执行文件都会由一个库的副本,必然会浪费系统空间。
重新编译:一旦发现库中有bug,挽救起来就比较麻烦,必须一一把链接该库的程序找出来,然后重新编译。
动态库嵌入到可执行文件中后,不用担心系统上没有该库。动态库在程序运行时被链接,故程序的运行速度和链接静态库相比打折扣。
编译成可执行文件
首先我们要编译test.c为目标文件,需要执行:
gcc –c –I /usr/dev/mysql/include test.c –o test.o
-I dir 把指定的目录dir加到检索头文件的目录表前面,因此,gcc会优先检索“-I”选项指定的目录,然后再检索标准的系统头文件目录.
连接
最后我们把所有目标文件连接成可执行文件:
gcc –L /usr/dev/mysql/lib –lmysqlclient test.o –o test
-lname 连接时从“-L”选项指定的目录和标准的库函数目录中检索指定的库函数libname.a
-L dir 把指定的目录dir加到供“-l”选项检索的目录表中。gcc将会优先检索“-L”选项指定的目录,然后再检索标准的系统库函数目录。
强制链接时使用静态链接库
默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usr/dev/mysql/lib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc –L /usr/dev/mysql/lib –static –lmysqlclient test.o –o test