剑,是一种武器,古龙先生很喜欢论剑,对剑也有不一样的理解;对于剑,使用它的是剑客,而如果要升级则是人的升级,从而推动剑术的提高与用剑的境界的提升。
好的剑都是铸剑师倾入自己的心血浇灌而成的;而对于剑客,要想能够使用它,不仅仅是挥动它,而是用能够与之沟通,能够“心灵相通”,不断地与之磨合,达到收发自如的境界。
编译器就是程序员的“剑”,而且GCC就是“剑中利器”;但是它对于所有人都是一样的,其关键在于使用它的人,如何去“磨”,如何去“练”,“磨”出自己的“形状”,“练”出自己的感觉,让自己得心应手的去使用它。
一)gcc的基本介绍,详细的使用情况,请查看附件或者gcc的官网手册——https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/
gcc 是GNU Compiler Collection的缩写,即GNU编译器工具集。在c语言的编译流程中主要扮演两个角色预处理器与编译器,另外,它也能够调用了binutils中的汇编器(as)与链接器(ld)生成对应的elf文件。它主要支持编译的语言有:c/c++/java/object-c/object-c++/fortran/ada.
gcc作为一个编译器,为了能够更好的整理各种工具,它被设计为一个由specs文件驱动的程序。specs文件描述了gcc的所有行为。默认的spec文件可以用”gcc -dumpspecs”,详细描述见https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Spec-Files.html#Spec-Files。通过spec文件可以理解gcc的执行流程与相关参数的设置,通过修改它可以配置它的执行流程。当然也可以定制spec文件,通过gcc -specs=<file>实现。
为什么要学习与熟悉gcc呢?不仅仅是因为它是完备的编译器,更因为GNU开源的强大力量,已经将它推广到各种处理器构架与各种应用平台。但是因为它的适用性很广,从而导致了它很多的命令项,让人头晕目眩,但是常用的确只有一些,被反复使用,值得总结一下。
gcc被编译出来,主要的可执行文件(LFS7.6:6.17.2):
c++/g++:c++编译器
gcc/cc:c编译器
cpp:c语言预处理器
gcov:程序覆盖测试工具,用于统计程序运行代码执行的频率,从而为程序优化做参考
二)gcc的基本用法
1.演绎c语言编译的流程(以hello.c为例):
a)预处理:gcc -E hello.c,将c语言定义的宏展开,输出到终端界面
b)编译:gcc -S hello.c,解析c语言语法生成对应的汇编语言(AT&T语法)文件,即hello.s
c)汇编:gcc -c hello.s ,调用as将编译生成的hello.s生成模板文件(elf文件),即hello.o
d)链接:gcc -o hello hello.o,调用ld将生成的hello.o链接成可执行文件,即hello
2.编译c语言源码的主要命令行参数:
在编译c语言过程中,需要注意两个方面:其一为头文件(*.h)的寻找,其二为函数库的链接。c语言中头文件的使用是一般是用相对路径,”#include <*.h>”指定包含的文件内容,但是从哪个路径下去寻找对应的头文件。需要用”-I path”的参数来指定对应的路径。当在链接时,如果碰到如下出错,“hello.c:(.text+0xf): undefined reference to `add'”表示无法找到对应的符号引用,则为链接出错。解决这样的问题是用“-L path”指定链接库的路径,“-l so-name”指定链接库的名(比如:libcurl.so,名称为curl)。
如上所诉,gcc指定编译路径与链接库的方式为:
gcc -I inc-path -L lib-path -l lib-name.
C语言中使用宏来帮助程序编写,为了提高程序兼容性,在许多软件项目用宏来分隔不同平台与使用环境的代码,从而当在实际编译过程中指定相应的宏来控制程序的编译过程。可以用“-D MACRONAME=VALUE”来将MACRONAME传递给程序,编译。
将调试信息添加到elf文件中,方便程序调试,建议是每次编译都把它打开,方便程序调试.用参数“-g”来指定。
优化程序指令“-O 2”,指定编译时的优化等级。
为了提前发现各种语法错误与逻辑错误,可以将“- Wall”所有的警告信息打印出来,然后对代码进行优化与纠正。
链接所有的目标文件,生成动态链接库:gcc -shared -fPIC -o lib$fn.so $fn.c
3.gcc的默认路径与行为:
a)如何获取gcc的默认链接路径与头文件路径(LFS7.6:5.4):
(1)编译测试的c语言源代码:echo 'main(){}' > dummy.c
(2)获取编译信息:gcc dummy.c -v -Wl,--verbose &> dummy.log
(3)查询默认头文件路径:grep -B4 '^ /usr/include' dummy.log
(4)查询默认的链接路径:grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'
(5)查询标准c的库路径(glibc):grep "/lib.*/libc.so.6 " dummy.log
(6)获取默认链接库的路径的方法:gcc -print-search-dirs |sed -n '/libraries:/p'
b)影响gcc行为的环境变量,格式跟PATH一样用”:”分割:
CPATH/C_INCLUDE_PATH:增加头文件搜索路径
LIBRARY_PATH:增加链接库的搜索路径
4.交叉编译工具,主要有三部分(binutils/gcc/glibc)介绍:
在开发主机上生成目标主机的编译工具,比如:在x86开发平台上生成arm板上运行的gcc——arm-none-linux-gnueabi-gcc;在gcc之前添加交叉编译前缀。对于交叉编译工具,形式上在编译器工具集之前添加了交叉编译前缀,使用方式和gcc是一致的,只是有一些针对目标机器的特定参数,实际效果是生成了能够在目标主机上运行的可执行文件,可以通过binutils进行分析。获取交叉编译工具的方法有三种:1.到网上下载现成的工具集;2,自己一步一步编译;3.用现成的编译脚本一次编译。
三)Makefile的基本介绍,详细见附件makefile文档
软件工程都是由很多模块化的源文件构成,当软件规模不断扩大,同时软件bug不断修改,也是为了简化与减少重复的软件劳动,需要有序地将所有软件源文件管理起来,从而诞生了Makefile。
了解Makefile对理解程序如何生成的与软件模块的构成有很大的帮助,而且往往一开始接触软件项目时,都有一套软件系统或者由自己构建软件系统,此时makefile就是第一个需要去熟悉的步骤。
首先makefile被make命令给读取,然后执行;make命令的常用参数:
基本格式:make [选项] [目标](如果目标不为空,则去执行makefile定义的目标)
-j N :同时允许 N 个任务;可以加快make的执行。
-C dir:进入dir目录,执行make命令
Makefile的基本书写格式:
目标:依赖
[Tab]命令
所有的makefile可以看作是定义的目标的集合。
当系统越来越复杂时,构建makefile也是很复杂的事,同时也考虑到软件系统编译时,也依赖于系统编译环境,所以就有了自动构建makefile的相关工具与命令:gnu发布软件的autotools(通过configure配置来生成相关的makefile),cmake来自动构建makefile,qt编译时使用的qmake等。在Android系统开发构成也引入了Android.mk与mm命令来编译系统。