预处理器
1. 什么是预处理器
预处理器是 编译过程中一个单独的步骤
预处理在编译之前进行,预处理之后的文件将会被输出给编译器
2. 预处理指令
2.1 替换文本宏
仿对象宏:
#define 标识符 替换列表
将之后每一个出现的标识符替换成替换列表(替换列表可以为空),示例:c#include <stdio.h> #define NUM 100 int main(void){ printf("%d", NUM); }
程序将会输出 100
仿函数宏:
#define 标识符(形参列表) 替换列表
当下文出现标识符紧跟一个”实参列表“的文本,将会被替换成替换列表
,同时替换列表
中与形参列表中的形参相同的文本将会被替换成”实参列表“中对应的实参 示例:c#define max(a,b) (((a)>(b))?(a):(b)) int main(void){ printf("%d",max(1,2)); }
max(1,2)
会被替换成(((1)>(2))?(1):(2))
因为宏是文本的替换,因此在替换过程中可能会导致一些运算符优先级问题,如下:c#define F(x) x+2 #define G(x) 3*x int main(void){ printf("%d", G(F(2))); }
此时
G(F(2))
被替换成3*F(2)
,F(2)
被替换成2+2
,因此G(F(2))
会被替换成3*2+2
!可能会造成与预期不同的结果,造成程序的错误。 因此,解决方案是把宏以及宏里面所有形参加括号:c#define F(x) ((x)+2) #define G(x) (3*(x))
#define 标识符(形参列表, ...) 替换列表
在替换列表中,额外实参会替换__VA_ARGS__
标识符 替换列表中可以包含标记序列__VA_OPT__(内容)
:当没有额外实参的时候,标记序列被替换为空,否则被替换为内容
示例:
c#define DEFINE_ARRAY(type, name, size, ...) type name[size] __VA_OPT__(= { __VA_ARGS__ }) int main(void){ DEFINE_ARRAY(int, arr, 4, 1, 2, 3, 4); }
DEFINE_ARRAY(int, arr, 4, 1, 2, 3, 4)
会被替换成int arr[4] = {1, 2, 3, 4}
# 运算符:把实参包含在引号中,变成一个字符串字面量:
示例:
c#define STR(a) #a int main(void){ printf("%s is %s", STR(mdr), STR("cute")); }
STR(mdr)
会被替换成"mdr"
STR("cute")
会被替换成"\"cute\""
## 运算符:连接两个记号
示例:
c#define FUN(x) int fun_##x(){return x;} FUN(2) int main(void){ printf("%d", fun_2()); }
FUN(2)
会被展开成int fun_2(){return 2;}
#define
的替换列表包含多行内容时,在每一行(除了最后一行末尾加\
示例:
c#define FUN(x) int fun_##x(){return x;} #define OUTPUT(str, ...) printf(#str __VA_OPT__(,) __VA_ARGS__) #include<stdio.h> FUN(2) int main(void){ OUTPUT(%d, fun_2()); }
展开后的结果:
c#include<stdio.h> int fun_2(){return 2;} int main(void){ printf("%d", fun_2()); }
程序会输出
2
#undef 标识符
:解除之前对标识符
的定义宏 不能 实现真正的递归或者重载
2.2 包含文件
include "文件名"
include <文件名>
(1)先搜索当前目录,搜索不到再搜索标准包含目录 (2)直接搜索标准包含目录
2.3 有条件编译
defined
运算符defined 标识符
:如果定义了标识符
,求值为 1,否则为 0#if
,#elif
,#else
,#endif
#elifdef
,#elifndef
本章内容对应 cppref 链接如下: