GCC
导语
GCC是GNU Compiler Collection的缩写,包含gcc、g++等编译工具。这个工具不仅包含编译器,还包含其他工具集,例如ar、nm等。今天简单的讲一讲这个工具。
简介
GCC能编译C、C++、Objective-C、Fortran、Java、Ada等语言。GCC可以在不同的硬件平台编译,即支持交叉编译。
GCC的c编译器是gcc,其命令格式为
Usage:gcc [options] file... |
GCC支持默认拓展名策略,下表是GCC下默认文件拓展名的含义。
GCC常用的编译器有
头文件和库文件的默认路径是
单个文件编译成执行文件
$gcc hello.c |
会在当前目录下自动生成文件名为a.out的可执行文件。
如果想要指定生成的可执行文件名,啧加一个-o参数
$gcc -o hello hello.c |
此时就会出现一个叫hello的可执行文件。
编译生成目标文件
编译的过程分为如图所示
源文件、目标文件和可执行文件是常用的三个词。源文件即是存放源代码的文件。目标文件是指经过了编译器的编译生成的二进制机器码,但是目标文件的一些函数过程没有相关的指示和说明,所以一般不可执行。可执行文件是目标文件与相关的库连接后的文件,它是可以执行的。
单个文件编译成可执行文件时,中间文件只在编译的过程中临时存在,在编译完成生成可执行文件之后,会删除掉目标文件。然而在大型项目中目标文件需要保留用于不同的编译目标。
$gcc -c (-o hello.o) hello.c |
GCC使用一个-c的选项来将源文件生成目标文件,而不是可执行文件,默认情况下生成的目标文件和源文件名称一样,拓展名为.o
如果要指定生成的目标文件的文件名,也是用-o的选项。
此外,可以用一条命令编译多个源文件。比如
$gcc -c file1.c file2.c file3.c |
多文件编译
不管是目标文件还是源文件,都可以用同一个命令编译到一个可执行文件之中。
比如我们有一个add.c和一个main.c,在main.c里有对add.c的调用,我们直接执行以下命令
$gcc -o test main.c add.c |
即可得到文件名为test的可执行文件。
当然我们也可以先把他们都编译成目标文件然后再链接
$gcc -c main.c |
甚至我们可以编译一个为目标文件,然后另一个为源文件使用
$gcc -c main.c |
都可以得到一个test的可执行文件。
预处理
在C语言程序中,一般会定义一些宏。预处理过程将源文件中的头文件包含进源文件,并且将文件中定义的宏进行脱焊
用下面的命令
$gcc -E string.c |
会告诉编译器进行预编译操作,如果要修改文件名,那么同样是加上参数-o
编译成汇编语言
用下面的命令
$gcc -S string.c |
编译string.c为汇编语言,文件名为string.s
静态链接库
静态链接库是obj文件的一个集合,同城以.a为后缀。静态库由程序ar生成。由于多数程序在使用动态库,静态库已经不再常见。
静态库的有点是可以在我不用重新编译程序代码的情况下,进行程序的重新链接,在编译大型程序的时候,这样可以节省很可观的时间。此外,静态库使开发者可以提供库文件给使用的人员,而不用开放源代码。
理论上,静态库的执行速度比动态库和共享库快1%到5%。
生成静态链接库
使用下面的命令
$ar -rcs libstr.a string.o |
使用工具ar将string.o打包为库文件libstr.a
使用下面的命令
$ar 库文件名 obj文件1 obj文件2 |
来生成静态库或者将obj文件加到静态库里。
使用静态链接库
只需要在gcc的文件里加上.a文件
$gcc -o test main.c libstr.a |
也可以使用命令-l 库名,库名时不包含函数库和拓展名的字符串。例如libstr.a的库名就是libstr.a
$gcc -o test main.c -lstr |
上面的命令将在系统默认的路径下查找str函数库。可能系统会提示无法找到库文件str,这是由于str不在默认路径下。我们可以用-L的参数来指定路径
$gcc -o test main.c -L./ -lstr |
是当库文件和当前编译文件在同一目录下。
动态链接库
动态链接库中存放的是目标文件,和静态链接库相比,目标文件在动态链接库中的函数和变量的地址是相对地址而不是绝对地址,真实地址在调用动态库的程序加载时形成。
动态链接库的名称有 别名(soname),真名(realname),和链接名(linker name)。别名形如libXX.so其中XX是库名。真名一般是别名价格小版本号或者发布版本。连接名是程序链接时使用的库的名字。动态链接库安装的时候,总是赋值库文件到某个目录下,然后用一个软连接生成别名,在库文件进行更新的视乎,仅仅更新软连接即可。
生成动态链接库
使用-fPIC选项或者-fpic选项,使gcc生成的代码是位置无关的,这样就能生成一个动态链接库。例如
$gcc -shared -Wl,-soname,libstr.so -o libstr.so.1 string.c |
-share告诉编译器生成一个动态链接库
-Wl告诉编译器后面的参数传递给链接器
-soname指定了动态库的soname
-o指定了动态库的真名
动态链接库的配置
动态链接库的使用需要指定系的动态链接库搜索路径,让系统找到运行所需要的动态链接库才行。/etc/ld.so.conf是动态链接库的搜索路径配置文件,里面存放着动态链接库所在目录的名字。
动态链接库的管理命令
ldconfig命令让新增加的动态链接库能够被系统共享。它可以在系统默认的搜索路径和动态链接库的配置文件中列出的目录中搜索动态链接库,创建动态链接装入程序需要的链接和缓存文件,搜索完毕后将结果写入/etc/ld.so.chche缓存文件。
ldconfig的用法如下,选项含义见表
ldconfig [-v|--verbose] [-n] [-N] [-X] [-f CONF] [-C CACHE] [-r ROOT] [-l] [-p|--print-cache] [-c FORMAT] [-V] [-?|--help|--suage] path... |
使用动态链接库
同使用静态链接库,使用”-l 库名”的方式引入动态链接库
注意 实际上,在编译过程中也可以用L来指定动态链接库的路径,并且编译不会报错,但是在运行时则会出现无法加载动态链接库的报错。这是因为不同于静态链接库将整个库的目标文件一并写入可执行程序,动态链接库只提供一个动态链接库的库名,程序在执行时,会根据我们系统目录中保存的库名和路径去装载动态链接库内的函数,并且 如果有多个程序调用同一个库,在内存中值装载一份该共享库的实例。
此外还要注意,如果系统的搜索路径下同时存在静态链接库和动态链接库,默认情况下会链接动态链接库,如果需要强制链接静态链接库,需要加上“-static”选项。
动态加载库
动态加载库类似于动态链接库,区别在于,动态链接库在程序启动时加载(load),而动态加载库则可以利用程序的方法来控制什么时候加载。动态加载库主要有dlopen()、dlerror()、dlsym()和dlclose()s。
dlopen()
void * dlopen(const char *filename, int flag); |
按照用户指定的方式打开动态链接库,其中参数filename为动态链接库的文件名,flag为打开方式,一般为RTLD_LASY,函数的返回值是库的指针。
dlsym()
void * dlsym(void *handle, char *symbol) |
获取动态链接库中指定的函数指针,然后可以用这个函数指针进行操作。handle为dlopen()打开库后返回的句柄,symbol为函数名称,返回值为函数指针。
GCC常用选项
在代码段里如果有
#if def MACRO |
那么
$gcc -D MACRO |
就可以指定这段被执行。
此外还有
-I dir:将头文件搜索路径扩大,包含dir目录
-L dir:将链接时使用的链接库搜索路径扩大,包含dir目录。
-static:使用静态库链接,gcc会优先使用动态库
-g:包括调试信息
-On:优化程序,程序优化后执行速度会更快。n一般意义如下
-O0:关闭所有优化选项
-O1:基本优化,编译器会生成更快的代码
-O2:-O1的升级版,推荐使用
-O3:这是目前最高的优化级别,它会使用更多的编译时间,虽然它生成的代码只会比-O2块一点点(GCC3.x中是这样的,但是在GCC4.x中有时候可能还没有-O2快),但是它会增大二进制文件的体积并让他们更消耗内存,因此在GCC4.x中-O3是不推荐的。
-Os:这个级别是用来优化代码尺寸的,他只是给一些CPU缓存或是磁盘空间小的机器使用
-Ofast:
该选项将不会严格遵循语言标准,除了启用所有的-O3优化选项之外,也会针对某些语言启用部分优化。如:-ffast-math ,对于Fortran语言,还会启用下列选项:
-fno-protect-parens
-fstack-arrays
-Og:
该标识会精心挑选部分与-g选项不冲突的优化选项,当然就能提供合理的优化水平,同时产生较好的可调试信息和对语言标准的遵循程度。
-W all:打开所有gcc能够提供的,常用的警告信息。
下表示具体的含义