33.1.源码到可执行程序过程
(1)源码.c文件->(编译)->elf可执行程序。
(2)源码.c->(编译)->目标文件.o->(链接)->elf可执行程序。
(3)源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序。
(4)源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序。
(5)预处理用预处理器,编译用编译器,汇编用汇编器,链接用链接器,这些工具再加上额外的会用到的工具组成编译工具链,gcc就是某个编译工具链。
33.2.预处理的意义及编程应用
(1)编译器本身的主要目的是编译源代码,将C的源代码转化成.S的汇编代码;编译器聚焦核心功能后,就剥离出某些非核心的功能到预处理器;预处理器帮编译器做一些编译前的杂事。
(2)预处理阶段的主要工作:头文件包含(#include);注释;条件编译(#if #elif #endif/#ifdef);宏定义。
(3)配置gcc实现只预处理不编译:gcc编译时可以给某些参数来设置,譬如gcc xx.c -o xx即指定可执行程序的名称,譬如gcc xx.c -c -o xx.o即指定只编译不链接,譬如gcc -E xx.c -o xx.i即实现只预处理不编译;通常情况下我们没必要只预处理不编译,但有时该技巧可帮助我们研究预处理过程,帮助debug程序。
(4)宏定义被预处理后的现象:第1宏定义语句本身不见了,则编译器根本就不知道#define宏;第2typedef重命名语言没变,则typedef是由编译器来处理的。
33.3.头文件包含
(1)#include<>和#include”“的区别:<>专门用来包含系统提供的头文件;”“用来包含程序员自己写的头文件。
(2)<>包含头文件:C语言编译器只会到系统指定目录(编译器/操作系统配置的目录,譬如在ubuntu中是/usr/include目录,编译器允许用-I来附加指定其它的包含路径)去寻找该头文件,若没找到则提示该头文件不存在,注意编译器不会在当前目录下寻找该头文件。
(3)”“包含的头文件,编译器默认会先在当前目录下寻找该头文件,若没找到则再到系统指定目录去寻找,若还没找到则提示该头文件不存在。
(4)总结:若包含系统指定的自带的头文件用<>;若头文件是自己写的存放在当前目录下用”“;若头文件是自己写的但集中存放在某个目录下将来在编译器中用-I参数来寻找,此时头文件包含用<>。
(5)头文件包含的真实含义:在包含头文件的那一行,将头文件的内容原地展开替换包含头文件的语句,替换过程在预处理中进行。
33.4.注释和条件编译
(1)注释是给人看的,不是给编译器看的;在预处理阶段时预处理器会拿掉程序中所有的注释语句,到了编译器编译阶段程序中已经没有注释了。
(2)条件编译即有时候我们希望程序有多种配置(源代码编辑阶段给每个配置开关编写了相应的源码),在源代码级别去修改配置开关来让程序编译出不同的效果。
(3)条件编译中条件判断标准:#ifdef和#if;#ifdef XXX(其判断标准是XXX该符号在本语句之前是否被定义,只要定义了则表达式成立(#define XXX或#define XXX 12或#define XXX YYY));#if(条件表达式)(判定标准是()中的表达式是否为true还是flase