作为一个程序猿小白,每次有困惑时看到别的阿猿码的博客,收益颇多,感叹他们的分享精神和技术能力之余,而对于自己写博客还是少点勇气,除了写作上的要求外,技术上能否深入浅出,不拘一格,把想明白的思路准确表达总结出来,也是很大的挑战,今天特为博客杀青,希望自己坚持下来,与君共勉!
今天在调试的时候遇到了有关gcc的编译优化的问题,觉得知识不总结,不明白它的前世(历史)今生(发展),总有一种“不知庐山真面,只缘身在此山中”的感觉,如果真的有人问:gcc乃何物?大脑里总不能除了“是个编译器”就再没有想法了!就像师兄花了将近一千美刀入手了一套TI的DSP2000系列的开发套件,也仅仅是停留在修改include别人的code(成果)的层次,试想每天有多少人把砖搬来搬去呢?
感谢开源,gcc(即GNU Compiler Collection)是GUN项目中符合ANSI C标准的编译系统,初学嵌入式的时候接触到的,它功能确实很强大,不仅能够编译用C、C++和Objective-C等语言写的程序;又是一个交叉平台编译器,能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,很适合在嵌入式领域(开发环境大多基于交叉编译)的开发编译。
①在Linux中也可以查询gcc的版本,命令如下:
$ cat /proc/version (p.s.14.04.1LTS版本应该是4.8.2)
②gcc所支持扩展名:
.c
c源程序
.C/.cc/.cxx
c++源程序
.m
Objective-C源程序
.i
已经过预处理的C源程序
.ii
已经过预处理的C++源程序
.s/.S
汇编语言源程序
.h
预处理文件(头文件)
.o
目标文件
.a/.so
编译后的库文件
要真正理解这些扩展名后缀,就得知道gcc的编译流程,其实man一下gcc手册就知道了,shell给的描述是:When you invoke GCC,it normally does preprocessing,compilation,assembly and linking.
而编译C++源程序时的基本语法:g++ [option] [file]
即 1)预处理pre-processing
这个阶段对#开头的预处理语句(文件包含/宏定义/条件编译)分别进行处理,使用“-E”选项告诉gcc只进行预编译不进行其他处理;
$ gcc -E exit.c -o exit.i
2)编译compiling
这个阶段gcc执行词法/语法/语义检查后把.i文件翻译为.s汇编代码,使用“-S”选项告诉gcc只进行编译而不进行汇编、链接等其他处理;
$ gcc -S exit.i -o exit.s
3)汇编assembling
这个阶段把.s汇编代码翻译成CPU可以识别的.o二进制文件,该文件又称为目标文件,使用“-c”选项告诉编译器只进行汇编而不做其他处理;
$ gcc -c exit.s -o exit.o
4)链接linking
即加载库;单个目标文件一般无法直接运行,链接阶段的任务就是把程序中所有的目标文件和所需的库文件链接在一起,最终生成一个可直接运行的可执行文件。
成功编译后进入链接阶段,会用到函数库,函数库定义了常用函数的实现部分,没有特殊的说明,gcc会到系统默认的路径/usr/lib下查找函数库,从而实现具体函数的执行,这个过程就是链接。有两点要注意:一是代码中函数的调用前一定要包含相关头文件,头文件中会对函数进行声明,否则编译也会警告;二是链接时的函数库分为.a静态库和.so动态库,静态库是完全加入到可执行文件中,生成代码较大,动态库只在程序执行的环节时加载,节省开销,所以gcc编译链接时默认使用动态库。
$ gcc -c exit.o -o exit
到此执行即可:$ ./ exit
故从扩展名的角度看程序编译过程为:
(源文件)hello.c->hello.i->hello.s->hello.o->hello(可执行文件)
③gcc编译选项总结:
gcc的可用选项我在man它的时候感觉在刷屏,太丰富了,不同的选项用于实现特定的操作,但是常用的记住,能灵活运用就可以了,以后需要的时候在慢慢积累吧
1)-E/-e -S -c :在刚才编译的每步过程中都用到了,不做赘述了;
2)-o file : 指定编译生成的可执行文件的文件名,调试方便,没有此选项编译时默认输出为a.out;
3)-w : 关闭所有的警告信息;
4)-x language :规定编译器编译源程序时所使用的语言;
5)-Wall:有warning all的说法,即打开gcc的所有的警告信息,以便最大限度实现可移植性;
6)-I dir:添加头文件的搜索路径dir;除了搜索Linux默认的路径/usr/include/添加头文件外,还可以添加放在其他位置的头文件,如使用第三方函数库时需要配套的头文件,从/root/test1/目录下搜索头文件来编译hello.
$ gcc hello.c -I /root/test1/ -o hello
7)-L dir : 添加库文件的搜索路径dir ;也可以添加使用第三方库函数(使用时要有配套的头文件)的路径dir
- lname:指定gcc链接名为name的库文件; -l 后紧跟的是库名(没有空格),而不是库文件名,如名为libtest.so的库文件,去掉头尾即库名为 test, 放在默认路径下的库文件直接用 -l 选项就能链接了;如果使用第三方库或库文件没有在默认路径下,就需要-L选项去添加库文件所在的dir目录了,如编译程序所加载的函数库libtest.so放在/home/sample目录下:
$ gcc hello.c -L /home/sample/ -ltest -o hello
p.s. 如不区分-I -l -L 三个编译选项的区别,编译很容易出错!!
8)-static
: 更改GCC的链接为静态库
9)-mcpu=type : 选择不同的cpu类型
10)-On : 控制优化代码的生成,n代表优化级别,一般为0~3;常规经验是调试阶段进行代码优化可能会出现意外,只有当程序最终发布时才考虑优化问题;
11)-O :让GCC编译时缩短代码的长度和执行时间;
12)-g :该选项把生成的调试信息添加到.0文件中,gdb根据这些信息就可以跟踪程序的执行状态;