GCC即GNU Compiler Collection,GNU编译套件,支持C、C++等多种语言。在使用GCC的时候,我们必须给出一系列必要的参数选项和文件名称。参数选项分几类,包括总体选项、语言选项、预处理选项、汇编选项、链接选项、目录选项、警告选项、调试选项、优化选项、目标选项、机器相关选项、代码生成选项等。
1、下面介绍一些最基本、最常用的选项。
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-Wpedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
上面列出了gcc的常用选项,其用法可概括为:
gcc [options] <filenames>
其中,options就是gcc所需要的选项,filenames给出相关的文件名称。
-c
:只编译或汇编源文件,但不进行链接,输出文件的后缀为.o
。
-S
:gcc在编译后停止,不进行汇编,输出文件的后缀为.s
。
-E
:gcc在预处理后停止,并输出预处理结果。
-std=standard
:指定C、C++标准,如c11
、c++14
、gnu11
、gnu++14
等。
-g
:产生调试信息,信息格式(如stabs、COFF、XCOFF、DWARF 2)因操作系统而不同。
-p
:生成额外的代码用以对prof
程序进行性能分析,编译和链接时都要使用这个选项。
-pg
:生成额外的代码用以对pgrof
程序进行性能分析,编译和链接时都要使用这个选项。
-Olevel
:指定优化程序的等级level,可以是1、2、3、0、s、fast、g,省略level时为1,优化之后,提高了程序执行效率,但编译速度会降低。
-w
:禁止所有警告信息。
-Wwarn
:警告相关,warn
并不是关键字,它有许多候选项,如all
表示产生大部分警告信息(一部分是很容易避免的),error
表示把警告信息当作错误来处理并停止gcc编译。
-Wpedantic、-pedantic
:输出ISO C/C++严格标准的所有警告,拒绝使用了禁用扩展的所有程序和一些不符合ISO C/C++规范的程序。
-Idir:添加头文件的搜索目录dir,先于系统目录。
-Ldir:添加库文件的搜索目录dir。
-Dmacro[=defn]
:定义宏macro,其值为defn或者省略。
-Umacro
:取消宏macro定义。
-foption
:通过-f
指定一些常用选项option
,如-fsigned-char
意思是把字符类型char
当作带符号的即signed char
。
-mmachine-option
:通过-m
指定机器相关的选项machine-option
,如-march=armv8-a+crc
意思是指定ARM的架构。
-o outfile
:指定输出文件的名称为outfile
,不指定时根据编译规则作默认处理。
@file
:从文件file中读取gcc命令行选项,这个选项使用时要谨慎。
infile
:指定gcc将要处理的文件。
-llibrary
:链接时搜索名字为library的库文件,如动态库libc.so
,对应的选项为-lc
。
-static
:在支持动态链接的系统上有效,链接时使用静态库(.a
,相关的环境变量是LIBRARY_PATH),而不是默认的动态库(.so
,相关的环境变量是LD_LIBRARY_PATH)。
-shared
:在某些系统上有效,生成动态库,与选项-fpic
一起使用。
-fpic
:意为位置独立代码,指示编译程序生成的代码要适合共享库的内容,这样的代码能够根据载入内存的位置计算内部地址。
-fPIC
:与-fpic
不同的是链接的可执行文件的GOT(global offset table)没有最大长度的限制。
-v
:打印gcc执行时执行的详细过程及其相关程序的版本号。
2、gcc根据文件名称的不同后缀做不同的事情。
.c
:C源码,必须预处理。
.i
:C源码,不用预处理,已经预处理过了。
.ii
:C++源码,不用预处理,已经预处理过了。
.h
:C、C++头文件。
.cc、.cp、.cxx、.cpp、.CPP、.c++、.C
:这些都是C++源码,必须预处理。
.hh、.hp、hxx、.hpp、.HPP、.h++、.H
:这些都是C++头文件。
.s
:汇编代码。
.sx、.S
:同样是汇编代码,但必须预处理。
另外,.a
为静态库,.so
为动态库,.o
为编译后的目标文件。
3、gcc编译的几个阶段,并通过file命令查看各阶段编译结果的文件格式。
// main.c
#include
int main()
{
printf("hello world\n");
return 0;
}
第一,预编译过程,这个过程处理宏定义和include,并做语法检查。
$ gcc –E main.c –o main.i
$ file main.c
main.c: C source, ASCII text
$ file main.i
main.i: C source, ASCII text
第二,编译过程,这个过程生成汇编代码i(main.s)。
$ gcc –S main.i
$ file main.s
main.s: assembler source, ASCII text
第三,汇编过程,这个过程生成目标代码(main.o)。
$ gcc –c main.s
$ file main.o
main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
第四,链接过程,这个过程生成可执行代码。
$ gcc main.o –o main
file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b27595ba2689cec1a5180a5f18ff9cd19930ac3c, not stripped
最后,运行程序。
$ ./main
hello world
另外,我们可以使用命令strip去除可执行文件的符号信息。
$ strip main
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=b27595ba2689cec1a5180a5f18ff9cd19930ac3c, stripped
4、编译静态库和动态库。
例子,将hello.c和hello2.c分别编译成静态库libhello.a和动态库libhello.so。
第一,编译静态库。
$ gcc –c hello.c hello2.c
$ ar –r libhello.a hello.o hello2.o
ar是个文件打包工具。
第二,编译动态库。
$ gcc –c –fpic hello.c hello2.c
$ gcc –shared hello.o hello2.o -o libhello.so
查看.o
文件、可执行文件、库文件的信息可使用如下命令:
nm - list symbols from object files.
objdump - display information from object files.
readelf - Displays information about ELF files.
5、查看预处理阶段使用了哪些宏。
gcc在预处理阶段会展开宏定义,使用如下两条命令都可以查看foo.c
在预处理阶段使用了哪些宏,包括gcc内置宏和用户自定义宏。
$ gcc -E -dM foo.c
$ cpp -dM foo.c