1.预处理指令:
程序员所编写的代码不能被真正的编译器所编译,需要先经过一段程序翻译一下
翻译的过程称为预处理,负责翻译的程序称为预处理器,被翻译的语句叫做预处理指令,以#开头的都是预处理指令
查看预处理的结果:
gcc -E code.c 把预处理的结果显示到终端
gcc -E code.c -o code.i 把预处理的结果存储到.i预处理文件
2.预处理指令的分类:
#include 头文件导入
#include <> 从系统指定路径查找头文件
#include "" 从当前工作路径查找,找不到再从系统指定路径查找
-I path 可以指定要查找的路径path
还可以通过设置环境变量来指定路径
#define 定义宏
宏常量:
#define MAX 50
优点:提高代码可扩展性、提高可读性、提高了安全性、还可以与case配合
注意:定义宏常量不要加分号,一般宏名全部大写
预定义好的宏常量:
printf("%s\n",__func__); 获取函数名
printf("%s\n",__FILE__); 获取文件名
printf("%d\n",__LINE__); 获取行号
printf("%s\n",__DATE__); 获取日期
printf("%s\n",__TIME__); 获取时间
宏函数:
是带参数的宏
不是真正意义的函数,没有发生传参,也没有返回值,也不会去检查参数的类型
#define SUM(a,b) a+b
1、先把在代码中出现了宏函数的位置,替换成宏函数后面的语句
2、再把代码中使用的参数替换成调用者的参数
注意:宏的内容必须保证在同一行,如果要换行,要在每一行的末尾添加续行符 \
宏函数的二义性:
由于宏函数代码位置、附近的值、参数各种原因的影响,会导致宏函数有不同的解释,这叫做宏的二义性
如何避免宏的二义性:
每个参数都加小括号,整体也叫小括号,不要在宏函数的参数中使用自变运算符
#define SUM(a,b) a+b //整体加小括号 可以避免二义性
#define SUM1(a,b) (a*b) //每个参数都加小括号 可以避免二义性
int main()
{
int n1=10,n2=20;
printf(“%d\n”,SUM(n1,n2)*2); //50 n1+n2*2 (n1+n2)*2
printf(“%d\n”,SUM1(n1+10,n2)); //210 (n1+10*n2) ((n1+10)*(n2))
}
3.条件编译:
根据条件决定让代码是否参与最终的编译
版本控制:
#if
#elif
#else
#endif
头文件卫士:防止头文件被重复包含,头文件必加
#ifndef 宏名 //如果宏不存在为真
#define 宏名
//
#endif
判断、调试:
#ifdef 宏名 //如果宏存在为真
#else
#endif
4.头文件中应该写什么:
头文件可能会被任意源文件包含,意味着头文件中的内容可能会在多个目标文件中存在,要保证合并时不要冲突
重点:头文件只编写声明语句,不能有定义语句
全局变量声明
函数声明
宏常量
宏函数
typedef 类型重定义
结构、枚举、联合的类型设计声明
头文件的编写规则:
1、为每个.c文件写一份.h文件,.h文件是对它对应的.c文件的说明
2、如果需要用到某个.c文件中的变量、函数、宏时,只需要把该文件的.h文件导入 即可
3、.c文件也要导入自己的.h文件,目的是为了让定义与声明保持一致
头文件的相互包含:
假如a.h包含了b.h的内容,而b.h中又包含了a.h的内容,这时就会产生头文件的 相互包含,无法编译通过
解决方案:把a.h中需要b.h的内容,和b.h中需要a.h的内容提取出来,额外再写 另一个c.h
5.Makefile:
Makefile是由一系列的编译器指令组成的可执行文件,叫做编译脚本
在终端执行 make 命令就会自动执行Makefile脚本中的编译指令,它可以根据文件的修改时间、和依赖关系来判断哪些文件需要编译,哪些不需要编译
需要一个名字叫做 Makefile 的编译文件
Makefile的编译规则:
1. 如果这个工程没有编译过,那么我们的所有c 文件都要编译并被链接。
2. 如果这个工程的某几个c 文件被修改,那么我们只编译被修改的c 文件,并重新链接目标程序。
3. 如果这个工程的头文件被改变了,那么引用了这几个头文件的c 文件都会重新编译,并链接目标程序。