在C++中,#define是一种预处理器指令,用于定义宏。宏是一种简短的文本,作用于预处理阶段,可以在程序中替换复杂的文本,以及用于条件编译。
语法
常见用法1:#define 标识符 字符串
#define WIDTH 10
#define HEIGHT (WIDTH + 10) // 这里的对括号很重要
int main()
{
int Area = WIDTH * HEIGHT; // 预处理时标识符将被替换
return 0;
}
上面的代码定义了两个宏 WIDTH 和 HEIGHT,在编译之前的预处理阶段,编译器会用替换源文件中标识符的每个匹配项:
int Area = 10 * (10 + 10);
而这里的对括号很重要,因为编译器只是简单地将标识符的匹配项进行替换,不会做多余的动作,对括号则控制了语句中的解释。若没有对括号,则代码将被替换为:
int Area = 10 * 10 + 10; // 结果将是110
另外,宏定义还可以用来代替一些字符串,比如:
#include <iostream>
using namespace std;
#define MyString "Hello World!"
int main()
{
cout << MyString << endl; // 输出Hello World!而不是MyString
return 0;
}
常见用法2:#define 标识符
与用法1相比,这里只有标识符,后面没有用于替换的字符串。标识符会保持定义,并且在预处理阶段也会移除源文件中标识符的匹配项。通常情况下,这类宏定义经常和#ifdef搭配用于条件编译。
#ifdef 标识符
// 如果标识符已经被定义,则到#endif之间的代码将被编译,否则将如同被注释一般
... ...
#endif
#include <iostream>
#define TEST
using namespace std;
int main()
{
#ifdef TEST
// 将被输出
cout << "The macro TEST has been defined" << endl;
#endif
#ifdef TEST_1
// 不会被输出
cout << "The macro TEST_1 has been defined" << endl;
#endif
return 0;
}
条件编译的一大作用是和#ifndef搭配防止头文件被多重包含。
#ifndef 标识符
// 这里是ifndef,表示如果标识符没有被定义,则这块代码将被编译,与#ifdef相反
#endfi
比如,头文件A.h包含了头文件B.h,而源文件C.cpp包含了头文件A.h和头文件B.h。预处理时会把头文件A.h的内容和B.h的内容都导入到源文件C.cpp中,而头文件A.h已经包含了B.h,也就是源文件C.cpp会有两份头文件B.h中的内容。如果没有条件编译,头文件B.h中声明的变量将会在源文件C.cpp中被重复声明。
// 头文件B.h
#ifndef _B_H
#define _B_H
// 头文件B的内容
...
#endif _B_H
源文件C.cpp经过预处理将会是:
源文件C.cpp
...
#ifndef _B_H
// 这块内容将被编译,因为此时标识符_B_H还没有定义
#define _B_H // 在这里定义了标识符_B_H
// 头文件B的内容
...
#endif _B_H
...
#ifndef _B_H
// 这块内容将不会被编译,因为标识符_B_H已经被定义
#define _B_H
// 头文件B的内容
...
#endif _B_H
...
常见用法3:#define 标识符(参数1, 参数...) 字符串
这类用法定义一个带有参数的、类似于函数的宏。通常用于定义有某个功能的代码段。比如:
#define ADD(num1, num2) (num1 + num2)
int main()
{
int number_1 = 1;
int number_2 = 2;
int add_result = ADD(number_1, number_2);
return 0;
}
其中,宏的形参列表里的参数名需唯一,中间用逗号隔开,需注意,形参列表的左括号与标识符之间不能有空格。在代码中调用该宏,需要参数列表能和形参列表匹配上,也就是参数的个数需一致。另外,由于这类用法常用于定义代码块,很多时候为了可读性并不能将代码块全部写在一行,这时候就需要用到换行符 \,如下:
#include <iostream>
using namespace std;
#define CheckIfPass(SCORE) \
if (SCORE < 60) \
cout << "Not pass" << endl; \
else \
cout << "Pass" << endl;
int main()
{
CheckIfPass(61); // 输出Pass
CheckIfPass(59); // 输出Not pass
return 0;
}
写在最后
预处理器指令#define定义宏在C++代码中很常见,也比较简单。在编译之前的预处理阶段,编译器就会根据定义的标识符,将源文件中匹配项进行替换,或是条件编译根据不同的情况编译不同的代码,甚至是配合预处理器定义的一些宏来控制代码在不同的调试模式下有不同的输出信息。
当然,一些情况下使用宏定义或许不是最合适的方法,像定义多行的宏函数,如果其中的逻辑较为复杂,行数过多,那么它的可读性则不够强,或许封装为一个函数更为合适。总之,合理地使用预处理器指令,能够指示编译器在预处理阶段帮我们处理代码,提高编程调试等效率。
最后,本文中提到的包含头文件#include和#ifdef、#ifndef等其它的预处理器指令,另外还有更多的预处理器运算符##、#@等等,会在本系列文章中后续更新。如果发现文章有任何错误的地方,欢迎批评指正,有任何疑问的地方,欢迎在评论区交流讨论!!!