编译过程是分为四个阶段进行的,即
预处理(也称预编译,Preprocessing)、
输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码。gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。
编译(Compilation)、
编译为汇编代码,预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
汇编 (Assembly)、
对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
连接(Linking)
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
gcc常用选项简要说明
想知道在其他非x86平台上gcc是如何工作的话,你可以有两种解决方案,可以自己man gcc
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-pedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...][-Umacro]
[-foption...][-mmachine-option...]
[-o outfile] infile...
-c 编译为目标文件,不连接库
-S 编译为汇编代码
-E 预处理.预处理之后的代码将送往标准输出
-Wwarn... 设置警告,可以设置的警告开关很多,通常用-Wall开启所有的警告
-Olevel 设置优化级别,level可以是0,1,2,3或者s,默认为-O0,即不进行优化处理.
-Dname=definition... 在命令行上定义宏,有两种方式,-Dname或者-Dname=definition.在命令行上设置宏定义的目的主要是为了在调试的时候设定一些开关,而在发布的时候再关闭或者打开这些开关即可,当然宏定义也用来对代码进行有选择地编译.另外也还有其他的一些作用.
-Uname 取消宏定义name,作用和上面的正好相反.
-Idir... 把dir加到头文件的搜索路径中,而且gcc会在搜索标准头文件之前先搜索dir.
-llibrary 在连接的时候搜索library库.库是一些archieve文件--其成员是目标文件.如果有文件引用library,library在命令行的位置应该在那个文件之后,因此,越底层的库越要放在后面.比如如果你要连接pcap库,那么你就需要使用-lpcap对源文件进行编译.
-Ldir... 把dir加到库文件的搜索路径中,而且gcc会在搜索标准库文件之前先搜索dir.
-pthread 通过pthreads库加入对多线程的支持,这为预处理和连接设置了标志.pthread是POSIX指定的标准线程库.
-std=standard 设置采用的标准,该选项是针对C语言的,比如-std=c99表示编译器遵循C99标准.该选项较少使用.而且有时反而会把你搞糊涂.
-o outfile 指定输出文件的文件名,默认为a.out
-mmachine-option... 指定所用的平台.
---------------------------------------------------------------------------------------
gcc常用选项总结
一、常规选项
1、没有任何选项:gcc helloworld.c
结果会在与helloworld.c相同的目录下产生一个a.out的可执行文件。
2、-o选项,指定输出文件名:gcc -o helloworldhelloworld.c
-o意思是Output即需要指定输出的可执行文件的名称。这里的名称为helloworld。
3、-c选项,只编译,不汇编连接:gcc -chelloworld.c
-c意思就是Compile,产生一个叫helloworld.o的目标文件
4、-S选项,产生汇编源文件:gcc -S helloworld.c
-S意思就是aSsemble,产生一个叫helloworld.s的汇编源文件
5、-E选项,预处理C源文件:gcc -E helloworld.c
-E意思就是prEprocess,产生一个叫helloworld.i的标准输出文件
输出不是送到一个文件而是标准输出。当然可以对它进行重定向:
gcc -E helloworld.c > helloworld.txt
二、优化选项
7、-O选项,基本优化:gcc -O helloworld.c
-O意思就是Optimize,产生一个经过优化的叫作a.out的可执行文件。也可以同时使用-o选项,以指定输出文件名。如:
gcc -O -o test helloworld.c
即会产生一个叫test的经过优化的可执行文件。
8、-O2选项,最大优化:gcc -O2 helloworld.c
产生一个经过最大优化的叫作a.out的可执行文件。
三、调试选项
9、-g选项,产生供gdb调试用的可执行文件:gcc -g helloworld.c
产生一个叫作a.out的可执行文件,大小明显比只用-o选项编译汇编连接后的文件大。
10、-pg选项,产生供gprof剖析用的可执行文件:gcc -pg helloworld.c
产生一个叫作a.out的执行文件,大小明显比用-g选项后产生的文件还大。
make
伪目标 P79 (伪目标会自动执行)
多目标 P80
静态模式 P81
自动生成依赖 P82
嵌套执行make P85 (经过命令行显式设置后,当make嵌套调用时,上层makefile文件中定义的变量会以系统变量的形式传递到下层的makefile文件中;默认情况下,只有通过命令行设置的变量会被传递,而定义在文件中的变量,如果要下层makefile文件传递,则要使用export)
定义命令包 P87 类似函数定义??
变量的高级用法 P91 1.变量值的替换 2.把变量的值再当作变量(操作符两边都可以,而且可以拼接)
追加变量要注意的点 防止递归定义(赋值变量的两种方式,后者可以防止递归定义,前者出现shell函数和wildcard函数的时候,因为makefile文件的隐式规则很可能会使性能收到无法估计的损害)
override指示符(这里有个小坑,如果make命令行参数设置的变量,在makefile文件中出现的同名变量的赋值会被忽略,除非用override指示符修饰)
define可以定义多行变量(一个变量名带着多行赋值包括换行符,有利于定义一系列的命令)
环境变量 (用make –e 能强制覆盖)< makefile变量 (override可以强制覆盖)< 命令行设置变量的关系(有点想全局变量和局部变量的区别) (link: 嵌套执行make)(注意点:1.SHELL和MAKEFILES(这是一个系统级环境变量,执行总控makefile文件是的make命令参数会在里面,上层的makefile文件中对他的定义也在里面)必定会往下传,想不往下传就用命令行将其值置为空,2.有几个参数是默认不会往下传)
目标变量和模式变量 P95
条件判断(注意点:make不允许把整个条件语句分成两个部分放在不同的文件夹中)
make中的函数
(1).字符串函数
$(word$(words <text>), <text>) //取出<text>中最后一个单词
(2)call函数
唯一可以创建新的参数化的函数(有点像重载)
(3)控制make的函数
指定文件和指定目标
如果make的命令行中不止一次使用-f(--file或--makefile)参数,所有指定的makefile文件会被连在一起传递给make执行
MAKECMDGOALS这个变量中存放所指定的终极目标(任何在makefile文件中的目标都可以被指定成终极目标的列表,但是除了以“-”打头或是包含了“=”的目标(因为它们会被解析成命令行参数或是变量),甚至没有被明确写出来的目标也可以成为make的终极目标(只要能找到隐含的推导规则,隐含目标也可以被指定成终极目标)
常用伪目标的含义:
all 这个伪目标是所有目标的目标,其功能一般是编译所有的目标
clean 这个伪目标功能是删除所有被make创建的文件
install 这个伪目标的功能是安装已经编译好的程序,其实也就是把目标执行文件复制到指定的目标中去(gcc的-o ?)
print 列出改变过的源文件
tar 把源程序打包备份,也就是一个tar文件
dist 创建一个压缩文件,一般是把tar文件压成Z文件或者是gz文件
TAGS 用于更新所有目标,已被完整地重编译使用
check和test 用来测试makefile文件的流程
一般makefile文件中最好写上这些目标
检查规则
-n
-t
-q
-w
隐含规则
隐含规则链
在默认情况下,中间目标和一般的目标有两点不同:第一个不同是除非是中间目标不存在才会引发中间规则;第二个不同是只要目标产生,过程中产生的中间目标文件会被rm -f删除。
make会优化一些特殊的隐含规则,而不生成中间文件。比如从文件foo.c生成目标程序foo,过程不会生成中间文件,实际情况是被一条cc命令完成(cc -o foo foo.c),中间过程在内存中完成。
?make会努力自动推导生成目标的一切方法,不管中间目标有多少,其都会执着地把所有的隐含规则和书写的规则全部合并起来分析,但是在隐含规则链中,禁止同一个目标出现两次或两次以上,防止在make自动推导中出现无限递归的情况。
?伪目标.INTERMEDIATE来强制显式声明一个文件或者目标是中介(默认情况下被makefile文件指定成目标或依赖目标的文件都不能当中介;伪目标.SECONDARY可以强制声明(如.SECONDARY : sec),阻止make自动删除中间文件或把目标以模式的方式来指定(如%.o)成伪目标.PRECIOUS的依赖目标,保存被隐含规则所生成的中间文件。
定义模式规则
“%”的展开发生在变量的函数展开之后,变量和函数的展开发生在make载入makefile文件时,而模式规则中“%”则发生在运行时。
总注意点:
1.foo.o : foo.p 如果只是写了这个依赖关系,没有写出显式规则,有可能这个依赖关系的说明就没用了,如果目录下存在foo.c,隐含规则生效,那么会通过foo.c调用c的编译器生成foo.o。因为在隐含规则中,Pascal的规则出现在c的规则之后,make找到生成foo.o的c的规则之后就不在寻找下一条规则了。
2.即使指定了-r参数,某些隐含规则还是会生效,因为有许多隐含规则都是使用另外后缀规则来定义。只要隐含规则中有后缀列表(也就是系统定义在目标.SUFFIXES的依赖目标),隐含规则就会生效。