预处理指令:
程序员所编写的代码不能被真正的编译器所编译,需要一段程序把代码翻译一下。
翻译的过程叫做预处理,执行翻译的程序叫做预处理器,被翻译的代码叫做预处理执行指令,以#开头的都是预处理指令
查看预处理结果:
gcc -E code.c 把预处理的结果直接显示到终端上
gcc -E code.c -o code.i 把预处理的结果存储到.i结尾的文件中
预处理指令的分类
#include 文件包含
#include < > 从系统指定的目录下查找头文件并导入
#include " " 先从当前目录下查找头文件,如果找不到再从系统指定目录查找并导入
注意:可以通过修改~/.bashrc 的环境变量来增加系统指定的目录
export C_INCLUDE_PATH=$C_INCLUDE_PATH:路径 但不建议修改
#define 定义宏
宏常量: #define MAX 100
优点:提高可扩展性(批量性修改)、提高了安全性(常量)、提高可读性、可以与case配合使用
注意: 一般宏名全部大写,末尾不要加分号
一般局部变量全部小写用下划线分隔、全局变量首字母大写、函数名全部小写用下划线分隔、宏名全部大写、指针变量+p、数组arr、字符串str
预定义的宏:
__func__ 获取函数名 %s
__FILE__ 获取文件名 %s
__DATE__ 获取当前日期 %s
__TIME__ 获取当前时间 %s
__LINE__ 获取当前行号 %d
宏函数:带参数的宏
不是真正的函数,不检查参数的类型,没有传参,没有返回值,只有计算的结果
#define SUM(a,b) a+b
1、把代码中使用的宏函数替换为宏函数后面的代码 a+b
2、把宏函数代码中使用的参数替换为调用者提供的参数
注意:宏函数不能直接换行,如果需要换行要在末尾加续行符 \
宏函数也可以使用大括号来保护代码
宏的二义性:
由于宏代码所处的位置、参数不同导致宏有不同的功能,这就叫做宏的二义性
如何避免宏的二义性:
宏函数整体加小括号、每个参数都要加小括号
注意:使用宏函数时不要提供带自变运算符的变量作为参数
注意:容易出选择题: 例如哪个宏有二义性,一步一步替换宏函数
练习1:实现一个交换两个变量的宏函数,通用性
#define swap(a,b) ...
常见笔试面试题:
1、#define 和 typedef 区别?
如果是普通类型,它们的功能没有任何区别
#define INT int
typedef int INT
如果是指针类型
#define INTP int*
typedef int* INTP
INTP p1,p2,p3;
#define 只有p1是指针变量,p2 p3都是int类型变量
typedef p1p2p3都是指针变量
2、 宏函数与函数的区别?
它们是什么?
宏函数:不是真正的函数,只是代码替换,只是用法像函数
函数: 一段具有某项功能的代码集合,会被编译成二进制指令,存储到代码段中,函数名就是该段内存的首地址、有独立的命名空间、栈空间
有什么不一样?
函数: 有返回值、类型检查、安全、入栈、出栈、速度慢、跳转
宏函数:运算结果、 通用、 危险、替换、 、速度快、冗余
优缺点?
条件编译
根据条件让代码是否参与最终的编译
版本控制
#if
#elif
#else
#endif
头文件卫士:防止头文件被重复包含
#ifndef
#define
#endif
判断、调试、上线 gcc code.c -DDEBUG -D 编译时定义宏
#ifdef
#else
#endif
定义打印调试信息的宏函数
#ifdef DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
定义打印错误信息的宏函数
#define error(...) printf("%s %s %s:%d:%m %s %s\n",__FILE__,__func__,__VA_ARGS__,__LINE__,__DATE__,__TIME__)
注意:以上两个宏函数,能会使用即可