复习:
1、文件包含
#include 把一个头文件导入到当前文件中
#include <>
#include ""
系统是通过环境变量指定头文件的加载路径
还可以通过编译参数 -I /path 指定头文件的加载路径
2、宏常量
#define 宏名 字面值数据
如果在代码中使用了宏,在预处理时会把所有的宏替换为宏名后面的字面值数据
优点:提高代码可读性、提高可扩展性、提高安全性、还可以与case配合使用
注意:宏名一般全部大写,末尾不要加分号,不能直接换行(需要使用续行符 \ )
预定义的宏:
__func__
__FILE__
__DATE__
__TIME__
__LINE__
3、宏函数
#define FUNC(arg) arg*10
不是真正的函数,就是带参数的宏,使用宏函数的位置会先替换成宏函数后面的代码,然后根据提供的参数替换掉对应参数的位置
注意:可以使用续行符和大括号来保护代码
二义性:
由于宏所处位置或参数不同可能导致宏有多种解释,这就叫宏的二义性
可以在最外层加小括号,每个参数都加小括号,以此降低产生二义性的可能,不要在宏中使用自变运算符
运算符:
# 把宏函数的参数变成字符串
## 合并两个参数变成标识符
4、条件编译
根据条件决定代码是否参与最终的编译
#if/#elif/#else/#ifdef/#ifndef/#endif
头文件卫士: 防止头文件被重复包含
#ifndef FILENAME_H
#define FILENAME_H
#endif//FILENAME_H
版本控制:
#if VERSION >= 3
#elif VERSION >=2
#elif VERSION >=1
#else
#endif
编译器判断:
#if __cplusplus
//C++
#else
//C
#endif
5、常考的笔试面试题:
定义一个宏表示100年有多少秒,忽略闰平年问题
#define SEC (3600*24*365*100u)
在类型重定义时#define与typedef的区别?
在定义常量时#define与const的区别?
宏函数与普通函数区别?
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}
头文件中应该写什么:
问题:头文件可能被任何的源文件包含,意味着头文件的内容会在多个目标文件中存在,合并时要保证不能冲突
重点:头文件中只能编写声明语句,不能有定义语句
全局变量声明 extern int num;
函数声明
宏常量
宏函数
typedef 类型重定义
结构、联合、枚举的类型声明
头文件的编写规则:
1、为每个.c文件写一份.h文件,.h文件是对.c文件的说明
2、如果需要使用某个.c文件中的变量、函数、宏、结构体...,只需要把该.c文件的.h文件导入即可使用
3、.c文件也要需要导入它的.h文件,目的是为了让声明与定义一致
头文件的相互包含:
假如a.h包含了b.h,b.h又包含a.h,这种情况就叫做头文件的相互包含,会导致编译错误
当确认变量、函数名已经定义且导入,但是依然报错:未定义 xxxx,先考虑是否头文件卫士写错,再考虑是否头文件相互包含
解决方法:从a.h中把需要用到的b.h内容提取出来,从b.h中把需要用到的a.h内容提取出来,放入新编写的c.h
Makefile
Makefile是由一系列编译指令组成的可执行文本文件,也叫编译脚本
在终端执行make命令就会自动执行Makefile文件中的编译指令,它可以文件的修改时间来判断哪些文件需要编译,哪些文件不需要重新编译,根据依赖情况判断哪些文件先编译、哪些后编译,从而提高编译效率
编译规则:
1、如果这个项目没有编译过,则所有的.c文件都要编译并被链接成可执行程序
2、如果某几个.c文件被修改,则此次只编译修改过的.c文件并链接生成可执行程序
3、如果某个.h文件被修改,依赖该头文件的所有.c文件全部重新编译并链接
一个最简单的Makefile脚本:
执行目标:依赖 xz
编译指令
被依赖的目标1:依赖的文件
编译指令
被依赖的目标2:依赖的文件
编译指令
被依赖的目标3:依赖的文件
编译指令
...
clean:
rm ...