GCC(GNU Compiler Collection,GNU编译器套件):是由GNU开发的编程语言编译器。GCC编译器套件支持多种语言的编译,包括了C、C++、Objective-C、Fortran、Java、Ada和Go语言的前端,也包括了这些语言的库。下面表格中列了些对于C、C++相关的主要软件包:
名称 | 功能 |
---|---|
cpp | C预处理器 |
gcc | C编译器 |
g++ | c++编译器 |
gccbug | 创建BUG报告的shell脚本 |
gcov | 覆盖测试工具,用于分析程序哪里做优化效果最佳 |
libgcc | GCC的运行库 |
libstdc++ | 标准的C++库 |
libsupc++ | 提供支持C++语言的函数库 |
$ gcc [选项扩展名] [文件名]
用于编译和链接C/C++程序所需的选项扩展名及介绍:
选项扩展名 | 文件内容 |
---|---|
.a | 静态库,由目标文件构成的文件库 |
.c | C语言源码,必须经过预处理 |
.C,.cc或.cxx | c++源代码文件,必须经过预处理 |
.h | C/C++语言源代码的头文件 |
.i | .c文件经过预处理后得到的C语言源代码 |
.ii | .C,.cc或.cxx文件经过预处理后得到的C语言源代码 |
.o | 目标文件,是编译过程得到的中间文件 |
.s | 汇编语言文件,是.i文件编译后得到的C++源码文件 |
.so | 共享对象库,也称动态库 |
注:以下列子以hello.c文件进行举例
(1)、预处理:C编译器对各种预处理命令进行处理,包括头文件包含、宏定义的扩展、条件编译的选择等。
预处理的gcc命令:
$ gcc -E hello.c -o hello.i
(2)、编译:将预处理得到的源代码文件进行”翻译转换”,产生出机器语言的目标程序,得到机器语言的汇编文件。
编译的gcc命令:
$ gcc -S hello.i
(3)、汇编:将汇编代码翻译成机器码,但是还是不可以运行。
汇编的gcc命令:
$ gcc -c hello.s
(4)、链接:处理可重定位文件,把各种符号引用和符号定义转换成为可执行文件中的合适信息,通常是虚拟地址。
链接的gcc命令:
$ gcc hello.o //默认生成a.out执行文件
$ gcc hello.o -o hello //通过-o参数命令指定生成新的执行文件名
注:链接分为动态链接和静态链接。
a、动态链接:使用动态链接库进行链接,生成的程序在执行的时候需要加载所需要的动态库才能运行。动态链接生成的程序小巧,但是必须依赖动态库,否则无法执行。可以说linux下的动态链接库实际是共享目标文件,一般是.so文件,类似于windows下的.dll文件。
b、静态链接:使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过体积较大。linux下静态库是汇编产生的.o文件的集合,一般以.a文件形式出现。
c、gcc默认是动态链接,加上-static参数则采用静态链接。
$gcc hello.o -static -o hello_static
表2 常用的GCC编译控制选项
名称 | 功能描述 |
---|---|
-c | 只编译不连接。编译器只是将输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件 |
-S | 只对文件进行编译,不汇编和链接 |
-E | 只对文件进行预处理,不编译汇编和链接 |
-o output_filename | 确定输出文件的名称为output_filename,这个名称不能和源文件同名,若不给出名称,将默认生成可执行文件a.out |
-g | 产生符号调试工具(GNU的gdb)所必要的符号信息,要想对源代码进行调试,就必须加入这个选项。g也分等级,默认是-g2,-g1是最基本的,-g3包含宏信息 |
-DFOO =BAR | 在命令行定义预处理宏FOO,值为BAR |
-O | 对程序进行优化编译、链接。采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、链接的速度就相应地慢一些 |
-ON | 指定代码的优化等级为N,可取值为0,1,2,3;O0没有优化,O3优化级别最高 |
-Os | 使用了-O2的优化部分选项,同时对代码尺寸进行优化 |
-I dirname | 将dirname目录加入到程序头文件搜索目录列表中,是在预编译过程中使用的参数 |
-L dirname | 将dirname目录加入到库文件搜索目录列表中 |
-l FOO | 链接名为libFOO的函数库 |
-static | 链接静态库 |
-ansi | 支持ANSI/ISO C的标准语法,取消GNU语法中与该标准相冲突的部分 |
-w | 关闭所有警告,不建议使用 |
-W | 开启所有gcc能提供的警告 |
-werror | 将所有警告转换为错误,开启该选项,遇到警告都会终止编译 |
-v | 显示gcc执行时的详细过程,GCC及其相关程序的版本号 |
在C程序中头文件的包含有两种方式:
(1)、#include
(2)、#include"stdio.h"
对于(1)使用尖括号的情况,预处理器会在系统预设的头文件包含目录搜索头文件
对于(2)使用双引号的情况,预处理会先在目标文件所在目录进行相应的头文件的搜索,如果当前目录没有,则会到系统预设的头文件包含的目录继续搜索。
注:在编译的时候也可以通过 -I dirname参数命令将指定的目录添加到头文件搜索目录列表中。
$ gcc -v hello.c -I /home/vmuser/hello
在实际产品开发过程中,往往会对某个产品的一些功能进行封装,以库文件的形式发布,给第三方用户使用。第三方用户拿到这个库文件,就必须在编译的时候将这个库链接到应用程序中。
库文件用法有两种:
(1)、在编译列表中写出库文件全名(可带路径),在这里假设一个静态库文件名为libsos.a/libsos.so,那么链接方式为:
$gcc hello.c libsos.a
$gcc hello.c libsos.so
(2)、分别用 -L指定库文件路径,并用 -l参数加上FOO名称即可,无需库文件全名。
a、将libsos.so所在目录添加到系统库文件搜索路径中,在编译的时候通过 -L dirname 完成。
$ gcc hello.c -L /home/vmuser/hello
b、指定链接库文件名,在编译的时候可通过 -lFOO参数将libsos.so链接到应用中:
$gcc hello.c -L /home/vmuser/hello -lFOO
注:使用了第三方库动态编译的可执行程序,在运行的时候还需要加载相应的库文件,但库文件的存放路径无需与编译路径一致,只要放在运行系统环境的库文件路径即可。
(1)、静态库是.o文件的集合,这些.o文件是编译器按照常规方法生成的,在Linux下也称文档(archive),用ar工具来管理。
下面以用两个C文件创建静态库为例:在用户主目录下,创建一个libhelloa目录,并在其中创建hello1.c和hello2.c两个文件。
将两个文件编译成目标文件:
$gcc -c hello1.c hello2.c
用ar命令创建静态库文件:
$ar -r libhello.a hello1.o hello2.o
(2)、共享库也是目标文件的集合,但这些文件是由编译器按照特殊方式生成的,对象模块的每个地址(函数调用和变量引用)都是相对地址,允许在运行时被动态加载和运行。
创建共享库首先需要编译对象模块。继续以hello1.c和hello2.c为例进行示范。
在用户主目录下,创建一个libhelloso目录,并在其中创建hello1.c和hello2.c两个文件。
$gcc -c - fpic hello1.c hello2.c
与创建静态库不同,这里加入了-fpic参数,表示生成的对象模块是可重定位的,pic表示位置独立代码。
编译完成,得到了hello1.o和hello2.o两个文件,再用下列命令生成共享库。
$gcc -shared hello1.o hello2.o -o libhello.so