预处理是在 程序编译之前进行的一步操作。
这个操作是 预处理之前 的操作,在 预处理 之前,编译器会对源代码会进行一些翻译操作:
\
) 后 紧跟 换行符 (回车键产生的字符)的 实例,并删除这些实例。token
) 序列,空白字符序列,注释序列。(token
:空格分隔的组) cout << "hello \
world" << \
endl;
// 根据 2, 上面的物理行 会被转换成
cout << "hello world" << endl;
int /*这里是注释*/ fox;
// 根据3, 上面的语句会转换成
int fox;
#line
#error
#pragma
预处理器指令
#
开头#
与指令的其余部分有空格,但是实际上并不行。#
开始,到第一个 换行符 为止,(指令的长度仅限于 一行逻辑代码)每个#define
行(逻辑行)由三部分组成:
#define
自身macro
)预处理器在程序中发现了宏的实例后,总会用 主体 替换这个宏。
宏展开: 从宏变成最终文本的过程。
#define TWO 2
#define FOUR TWO*TWO
int main(){
x = FOUR;
return 0;
}
// 替换过程为
// x = TWO*TWO;
// x = 2*2;
语言符号
从技术方面看,系统将 宏的 主体 当作语言符号(token
)类型字符串,而不是字符型字符串。
C预处理器中的 语言符号 是宏定义主体中 单独的词(空格分割开的词)。
// 这个定义的主体中 只有一个语言符号(token)即 2*3
#define SIX 2*3
// 这个定义的主体中,有三个语言符号,2 * 4,主体中的空格看作 分割语言符号的 符号
#define EIGHT 2 * 4
// 这个和 上面那个是一样的,额外的空格不看做主体的一部分
#define EIGHT 2 * 4
类函数宏
// 括号一定要贴着 PRINT!!!!
#define POWER(x) x*x
注意:
...
和 __VA_ARGS__
#include <iostream>
using namespace std;
void add(int i, int j = 1) {
cout << i + j << endl;
}
#define XNAME(...) add(__VA_ARGS__)
int main() {
XNAME(1);
XNAME(1, 4);
}
预处理器发现 #include
指令后,就会寻找 文件名 并把 这个文件内容 包含到 当前文件中。被包含文件中的文本将替换源代码文件中的 #include
指令。
#include <iostream> // 尖括号代表 搜索系统目录
#include "myHeader.h" // 引号代表,先搜索当前目录,再搜索系统目录
#include "/usr/biff/p.h" // 搜索 /usr/biff 目录
用来取消 #define
的宏定义。
#define LIMIT 100
#undef LIMIT
宏的作用域 从 #define 开始,到 #undef
或文件尾 结束。
#ifdef, #else, #endif
#ifdef MAVIS //如果定义了,执行下面的语句,否则到 else 中执行
#include "horces.h"
#else
#include "birds.h"
#endif
#ifdef HH
#include "didi.h"
#endif //必须存在的
#if, #elif
# if SYS==1
#define SYS_
#elif SYS==2
#define SYS__
#endif
#define NE 10
int main() {
// 有无空格都可以
#if NE > 10
cout << __DATE__ << endl;
}
#else
cout << __FILE__ << endl;
}
#endif
#
与 ##
的用法#
的作用#
的功能是将其后面的 宏参数 进行字符串化操作, 就是:宏变量替换后,左右各加一个双引号。(将语言符号转换成字符串!!!)
// #x 之间有无空格都可以
#define TEST(x) \
#x
int main() {
cout << "hello" << endl;
cout << TEST("hello") << endl;
}
// 输出结果为:
// hello
// "hello"
// 可以看到 # 的作用是 加 双引号。
##
的作用##
称之为 连接符(concatenator
),将两个语言符号组合成单个语言符号。这里连接的对象是 Token
就行,不一定非要是 宏变量。
N
个 ##
连接 N+1
个 Token
。
// 注意,x##n 有无空格都可以
#define XNAME(n) x ## n
#include <iostream>
using namespace std;
int main() {
// XNAME(4) 会变成 x4
int XNAME(4) = 3;
cout << x4 <<endl;
}
__VA_ARGS__
: 可变宏用到__DATE__
: 当前的时间__FILE__
:当前文件名__TIME__
: 源文件编译时间[C Primer Plus 5] p446