宏 #define (#) (##)
前言 (记得看)
宏属于编译器预处理的范畴,属于编译期概念(而非运行期概念)。 所有的预处理器命令都是以井号(#)开头。
**宏延续运算符(\)**一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。
预处理内容后续会写。
1、宏的用法
1.1、初级用法:无参宏定义
对于#define不带参数的情况,#define只做简单的文本替换,比如
#define PI 3.14
cout<<PI;
//输出结果为:3.14
#undef PI //撤销宏定义
1.2、高级用法:带参宏定义
参数只能是类似函数的方式去定义
#include <stdio.h>
#define MIN(a,b) (a<b?a:b)
int main(void){
cout<<MIN(3,5);
return 0;
}
//3
因此,可以用宏定义的形式去定义一些类似函数的代码
#define err_a(A) \
do{\
printf(A);\
}while(0)
err_a("error");
细节:被定义的内容尽量括起来
1.3、防止头文件重复包含
这里实际上是预处理的内容,假设你定义了一个头文件 head.h 文件多时很难避免头文件重复包含,此时可以在头文件里这样写
#ifndef _HEAD_H_
#define
xxxxxx
#endif
其中head.h 被抽象化成 _ HEAD_H_ 这样在重复引用头文件时,会优先询问是否定义head.h ,然后再决定是否执行xxxxxx。
1.4、(#) 和 (##)
(#) 运算符是对宏的参数的字符串化处理,简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
#define f(str) str
int a=1;
cout<<f(a);//替换为cout<<a;
//1
#define f(str) #str
int a=1;
cout<<f(a);//替换为cout<<"a";
//a
但是(#)会对空格做出处理,如果前面有多个空格会自动删掉,如果中间有多个空格会保留一个
#define f(str) #str
cout<<f( a a);//替换为cout<<"a a";
//a a
如果(#) 运算符出现在“ ”中,则需要再加双引号来区分出来,如下:
(##) 运算符是连接符,就是将两边的字符串拼接起来,拼接起来的字符串同时也会根据宏去改变
#define PRINT(n) printf("token" #n " = %d", game##n)
int token9 = 9;
int game9 = 99;
PRINT(9);
// 相当于:
printf("token9 = %d", game9);
//输出为:
token9 = 99
#define A(x) T_##x
则 int A(1) = 10; //等效于int T_1 = 10;
#define A(x) Tx##__
则 int A(1) = 10; //等效于int T1__ = 10;
1.5、可变的宏
可变的宏是具有可变数目参数的宏。这些参数由省略号代表,被保存在一个由逗号分隔的字符串中作为变量__VA_ARGS__,它会在宏的内部进行扩展。例如,下面的宏接受任何数目的参数:
#define err(...) fprintf(stderr, __VA_ARGS__)
int main() {
err("%s %d\n", "err",6);
return 0;
}
// fprintf(stderr,"%s %d\n", "err",6);
可变的宏可以包含命名的参数(只要随后有参数的变量长度列表) 。例如,下面的宏有两个固定参数,以及一个变量列表:
#define err(a,b,...) fprintf(stderr, "%s %d\n", a,b);\
fprintf(stderr, __VA_ARGS__);
int main() {
err("xxx",7,"%s %d\n", "err",6);
return 0;
}
2、注意细节
2.1、可能是测试使用习惯
#define N 5
#define M N+1
cout<<M*M;
//11
//cout<<M*M;-> cout<< 5+1*5+1;