C++中的宏是一种预处理指令,用于在编译时将代码中的标识符替换为指定的文本。
#define 指令
1.无参宏定义
无参宏的宏名后不带参数。
其定义的一般形式为:
#define 标识符 字符串
其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。
例如:
#define MAXNUM 99999
这样MAXNUM就被简单的定义为99999。
2.有参宏定义
C++语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define 宏名(形参表) 字符串
在字符串中含有各个形参。在使用时调用带参宏调用的一般形式为:宏名(实参表);
#define add(x, y) (x + y)int main(){//输出“1 plus 1 is 2.5.”cout << "1 plus 1 is " << add(1, 1.5) << ".\n";system("pause");return(0);
}
这个“函数”定义了加法,但是该“函数”没有类型检查,有点类似模板,但没有模板安全,可以看做一个简单的模板。
注意:该“函数”定义为(a + b),在这里加括号的原因是,宏定义只是在预处理阶段做了简单的替换,如果单纯的替换为a + b时,当你使用5 * add(2, 3)时,被替换为5 * 2 + 3,值为13,而非5 * (2 + 3),值为25。
#ifndef 指令
使用 ifndef 防止头文件被重复包含和编译。这是宏定义的一种,它可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等等.实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的一种----条件编译。 C语言或C++在对程序进行编译时,会先根据预处理命令进行“预处理”。C语言和C++的编译系统包括预处理,编译和链接等部分。
#ifndef x //先测试x是否被宏定义过
#define x //如果没有宏定义下面就宏定义x并编译下面的语句
...
...
...
#endif //如果已经定义了则编译#endif后面的语句
条件指示符 #ifndef 检查预编译常量在前面是否已经被宏定义。如果在前面没有被宏定义,则条件指示符的值为真,于是从 #ifndef 到 #endif 之间的所有语句都被包含进来进行编译处理。相反,如果 #ifndef 指示符的值为假,则它与 #endif 指示符之间的行将被忽略。条件指示符 #ifndef 的最主要目的是防止头文件的重复包含和编译。
千万不要忽略了头文件的中的 #ifndef ,这是一个很关键的东西。比如你有两个C(或C++)文件,这两个C(或C++)文件都 include 了同一个头文件。而编译时,这两个C(或C++)文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
所以还是把头文件的内容都放在 #ifndef 和 #endif 中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
...
#endif
<标识>
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如(这里以C语言举例子,因为这一讲的内容同时适用于C和C++):stdio.h
#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif#ifndef xxx //如果没有定义xxx
#define xxx //定义xxx
#endif //结束如果
这个用法主要是在头文件中,主要是为了防止类重复的 include
,所以在类的头文件之前加上前面两个,用类名替代 xxx
,在最后加上最后一句。
#undef指令
#undef是用来撤销宏定义的,用法如下:
#define PI 3.141592654
...
// code
#undef PI
//下面开始 PI 就失效了
宏定义相关作用符
换行符 \
我们定义宏语句或者宏函数时有很多条语句时怎么办?在每行末尾(除了最后一行)加上 \
,代表换行的意思。
#include <cstdio>
#define Print printf("这是第1条语句\n");\printf("这是第2条语句\n");\printf("这是第3条语句\n")#define Show(str1,str2,str3)\
{\printf("%s\n",str1);\printf("%s\n",str2);\printf("%s\n",str3);\
}
using namespace std;int main()
{Print; //无参数宏函数Show("first","second","third"); //带参数宏函数return 0;
}
执行结果:
字符串化符 #
#
是“字符串化”的意思,把在宏定义中跟在 # 后面的参数转换成一个字符串。
#include <cstdio>
#define Print(str)\
{\printf(#str"的值是%d",str);\
}
using namespace std;int main()
{int x=3,y=4;Print(x+y); //此处等价于printf("x+y""的值是%d",x+y);//#str等价于"x+y",所以#str不需要再用双引号引起来 return 0;
}
片段连接符 ##
## 是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些 ## 来替代空格。
#include <cstdio>
#define Add(n,value)\
{\num##n+=value;\
}
using namespace std;int main()
{int num1=1;int num2=10;Add(2,10); //等价于num2+=10; 这里把num和2连接成了num2 printf(" num1=%d\n num2=%d",num1,num2); return 0;
}
参考C++入门教程(十一、宏)_c++ 宏-CSDN博客