一、预处理器
预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。
所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
二、语法
#define 预处理指令用于创建符号常量。该符号常量通常称为宏,指令的一般形式是:
#define marco-name replace-text
三、原理
考虑以下代码:
#include<iostream>
using namespace std;
#define PI 3.1415926
int main(){
cout<<PI;
return 0;
}
及
#include<iostream>
using namespace std;
const double PI=3.1415926;
int main(){
cout<<PI;
return 0;
}
它们的运行结果都是3.1415926,有什么不同呢?
对于第一段代码,如果我们对其编译(GCC),使用 -E 选项进行编译,并把结果重定向到 test.p。打开test.p,将会看到它已经包含大量的信息,而且在文件底部的值被改为如下:
$ gcc -E test.cpp > test.p
...
int main (){
cout<<3.1415926;
return 0;
}
而第二段代码编译后却与源代码一样。
可以看出,#define的原理是将define以后的全部marco-name替换成replace-text。
三、参数宏
您可以使用 #define 来定义一个带有参数的宏(类似于函数):
#include<iostream>
using namespace std;
#define MIN(a,b) (a<b)?a:b
int main(){
cout<<MIN(5,10);
return 0;
}
程序输出51,需要注意,参数宏不需要return语句,因为define的本质是替换,所有上述代码编译后实际是:
...
int main(){
cout<<(5<10)?5:10;
}
在宏中没有;,但有时可能需要:
#include<iostream>
using namespace std;
#define OUTPUT(s) cout<<s;
int main(){
OUTPUT(114514);
cout<<endl;
OUTPUT("Hello World!\n");
return 0;
}
/*输出:
114514
Hello World!
*/
在这里,分号是必要的。
四、类型宏
如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a,b,c;
....
这里的#define对于许多oier应该不陌生,作用即将以下所有int换为long long,防止有时手残写错,需要注意,此时main()必须声明为signed main(),signed即有符号整形,因为main()不能是int_64。
五、#和##运算符
# 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。
注意:#和##运算符不可重载
#运算符
# 运算符(前置一元运算符)会把 replacement-text 令牌转换为用引号引起来的字符串。
#include<iostream> using namespace std; #define ToSTR(x) #x int main(){ cout<<ToSTR(Hello World!); return 0; }结果:
Hello World!看看它是如何工作的,编译这句:
cout<<ToSTR(Hello World!);成了:
cout<<"Hello World!";##运算符
##运算符(二元运算符)用于连接两个令牌。
#include<iostream> using namespace std; #define CONNECT(a,b) a##b int main(){ int xy=114514; int x=123,y=456; cout<<CONNECT(x,y); return 0; }结果:
114514看看它是如何工作的,同理,编译器将:
cout<<CONNECT(x,y);转换成了:
cout<<xy;
六、预定义宏
C++提供了以下预定义宏2:
| 宏 | 作用 |
|---|---|
| __LINE__3 | 返回当前行号 |
| __FILE__ | 返回当前文件名 |
| __DATE__ | 返回一个形同month/day/year的字符串,表示日期 |
| __TIME__ | 返回一个形同hour:minute:second的字符串,表示时间 |
例:
#include<iostream>
using namespace std;
int main(){
cout << "__LINE__:" << __LINE__ << endl
<< "__FILE__:" << __FILE__ << endl
<< "__DATE__:" << __DATE__ << endl
<< "__TIME__:" << __TIME__ << endl;
return 0;
}
结果:
__LINE__:4
__FILE__:D:\VS\project\test\main.cpp
__DATE__:Nov 26 2021
__TIME__:12:24:23
七、换行
对于一般宏,编译器忽略marco-name和replace-text间的空格,以换行结束。
你可以这么定义一个有换行的宏:(类似于多行字符串)
#define outp(a) int x;\
cin>>x;\
a=x;
编译器将忽略行末\,并将下一行的代码也算作宏。
实际上,对于很多换行之处(如字符串、宏、函数声明、函数调用、编译提示等)都可以这么做。
八、取消宏
可以使用#undef来取消宏定义并重新定义(没有undef前不能重定义):
#define N 114514
cout<<N<<endl;
#undef N
#define N 2233
cout<<N;
输出:
114514
2233
九、作用域
不同于常量(const),宏在自定义开始到程序结束或#undef前都有效,不受函数域限制:
void func() {
const int x = 123;
#define X 456;
printf("const x in func():%d\n", x);
cout << X;
}
int main() {
func();
printf("const x in main():%d\n", x);//非法
cout << X;
}
@HaohaoCppDebuger|寻兰
2021/11/26
THANK YOU !