预处理器是一些指令,指示表一起在实际编译之前所需要完成的预处理。
所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预指令处理之前。预处理指令不是C++语句,所以他们不会以分号(;)结尾。
#define预处理
#define预处理指令用于创建符号常量。该符号常量通常称之为宏,指令的一般行为为
#define macro-name replacement-text
当这行代码出现在一个文件中,在该文件中后续出现的所有宏都会在程序编译之前被替换成成replacement-text。
/*** define.cpp ***/ #include<iostream> using namespace std;#define PI 3.14159int main() {cout << "Value of PI :" << PI << endl;return 0; }
使用-E选项进行编译,并把结果重定向到define.p 现在查看一下相关信息:
exbot@ubuntu:~/wangqinghe/C++/20190816$ gcc -E define.cpp > define.p
条件编译
有几个指令可以用来有选择地对部分程序源代码进行编译,这个过程称之为条件编译。
条件预处理器地结构与if选择结构很像。
#ifdef NULL#define NULL 0 #denif
在调试时进行编译,调试开关可以使用一个宏来实现
#ifdef DEBUGcerr << “Variable x = ” << x << endl; #endif
如果在指令 #ifdef DEBUF 之前定义了符号常量,则会对程序中cerr语句进行编译。可以使用 #if 0 语句注释掉程序的一部分。
#if 0 不进行编译的代码 #endif
实例:
/*** ifdef.cpp ***/ #include<iostream> using namespace std; #define DEBUG#define MIN(a,b) (((a) < (b)) ? a : b)int main() {int i,j;i = 100;j = 20; #ifdef DEBUGcerr << "Trace: Inside main function" << endl; #endif#if 0cout << "MKSTR(HELLO C++)" << endl; #endifcout << "The minimum is " << MIN(i,j) << endl;#ifdef DEBUGcerr << "Trace: Comint out of main function" << endl; #endifreturn 0; }
运行结果:
exbot@ubuntu:~/wangqinghe/C++/20190816$ g++ ifdef.cpp -o ifdef
exbot@ubuntu:~/wangqinghe/C++/20190816$ ./ifdef
Trace: Inside main function
The minimum is 20
Trace: Comint out of main function
#和##运算符
#和##预处理运算符在C++和ANSI/ISO C中都是可用的
#运算符会把repalcement-text令牌转换成引号引起来的字符串
/*** pound.cpp ***/ #include<iostream> using namespace std;#define MKSTR(x) #xint main() {cout << MKSTR(HELLO C++) << endl;return 0; }
运行结果:
exbot@ubuntu:~/wangqinghe/C++/20190816$ g++ pound.cpp -o pound
exbot@ubuntu:~/wangqinghe/C++/20190816$ ./pound
HELLO C++
C++预处理器把下面这行
cout << MKSTR(HELLO C++) << endl;
转化成了:
cout << “HELLO C++” << endl;
##运算符用于连接两个令牌
#define CONCAT(x,y) x ## y
当CONCAT出现在程序中,他的参数机会被连接起来,并用来取代宏,程序中CONCAT(HELLO C++)会被替换成“HELLO C++”如下图:
/*** concat.cpp ***/ #include<iostream> using namespace std;#define concat(a,b) a##b int main() {int xy = 100;cout << concat(x,y) << endl;return 0; }
运行结果:
exbot@ubuntu:~/wangqinghe/C++/20190816$ g++ concat.cpp -o concat
exbot@ubuntu:~/wangqinghe/C++/20190816$ ./concat
100
C++预处理器把下面这行:
cout << concat(x,y) << endl;
转化成了:
cout << xy;
C++中的预定义宏
C++提供了下表所示的一些预定义宏:
宏 | 描述 |
__LINE__ | 这会在编译时包含当前行号 |
__FILE__ | 这会在编译时包含当前文件名 |
__DATA__ | 这会包含一个形式为month/day/year的字符串,它表示把源文件转换成目标代码的日期 |
__TIME__ | 这会包含一个形式为hour:minute:second的字符串,它表示程序被编译的时间 |
实例:
/*** macro.cpp ***/ #include<iostream> using namespace std;int main() {cout << "Value of __LINE__ :" << __LINE__ << endl;cout << "Value of __FILE__ :" << __FILE__ << endl;cout << "Value of __DATE__ :" << __DATE__ << endl;cout << "Value of __TIME__ :" << __TIME__ << endl;return 0; }
运行结果:
exbot@ubuntu:~/wangqinghe/C++/20190816$ g++ macro.cpp -o macro
exbot@ubuntu:~/wangqinghe/C++/20190816$ ./macro
Value of __LINE__ :6
Value of __FILE__ :macro.cpp
Value of __DATE__ :Aug 16 2019
Value of __TIME__ :14:06:20
# 和 ## 运算符
# 字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
当用作字符串化操作时,# 的主要作用是将宏参数不经扩展地转换成字符串常量。
宏定义参数的左右两边的空格会被忽略,参数的各个 Token 之间的多个空格会被转换成一个空格。
宏定义参数中含有需要特殊含义字符如"或\时,它们前面会自动被加上转义字符 \。
## 连接符号,把参数连在一起。
将多个 Token 连接成一个 Token。要点:
它不能是宏定义中的第一个或最后一个 Token。
前后的空格可有可无。