宏
宏定义
#define命令还可以定义带参数的宏定义,用于实现某种特定的功能,其定义型式为:
1 | //不带参数宏 |
不过,由于C++增加了内联函数(inline),实现起来比带参数的宏更方便,这样的宏在C++中已经很少使用了。
优点
- 提高了程序的可读性,同时也方便进行修改;
- 提高程序的运行效率:使用带参的宏定义既可完成函数调用的功能,又能避免函数的出栈与入栈操作,减少系统开销,提高运行效率;
- 宏是由预处理器处理的,通过字符串操作可以完成很多编译器无法实现的功能。比如##连接符。
缺点
- 由于是直接嵌入的,所以代码可能相对多一点
- 嵌套定义过多可能会影响程序的可读性,而且很容易出错;
对带参的宏而言,由于是直接替换,并不会检查参数是否合法,存在安全隐患。
补充: 预编译语句仅仅是简单的值代替,缺乏类型的检测机制。这样预处理语句就不能享受C++严格的类型检查的好处,从而可能成为引发一系列错误的隐患。
的确,宏定义给我们带来很多方便之处,但是必须正确使用,否则,可能会出现一些意想不到的问题。
1 可变参数宏
变参宏可以传递变参,类似于函数,如
1 |
- 第一个宏中由于没有对变参起名,我们用默认的宏VA_ARGS来替代它。
- 第二个宏中,我们显式地命名变参为args,那么我们在宏定义 中就可以用args来代指变参了。
- 同C语言的stdcall一样,变参必须作为参数表的最后一项出现。
当上面的宏中我们只能提供第一个参数templt 时,C标准要求我们必须写成:
1 | myprintf(templt,); |
总结:
1 | //gcc特色的“双井号”(##),是用于解决尾随逗号问题的,例如: |
1.2 宏的运算符
1.2.1 换行符
作用:当定义的宏不能用一行表达完整时,可以用\
表示下一行继续此宏的定义。
1 |
|
输出
1 | Zhangfei 你好 |
1.2.2 字符串化运算符
#
将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串;
1 |
|
注意, 对空格的处理:
忽略传入参数名前面和后面的空格。
1
2如:str=example1( abc );//将会被扩展成 str="abc"
当传入参数名间存在空格时,编译器将会自动连接各个子字符串,用每个子字符串之间以一个空格连接,忽略剩余空格。
1
如:str=exapme( abc def);//将会被扩展成 str="abc def"
1 |
|
分析:
- 调用
checkRuntime(cudaMalloc(&device_ptr, size));
时展开为__check_cuda_runtime(cudaMalloc(&device_ptr, size), "cudaMalloc(&device_ptr, size)", __FILE__, __LINE__)
1.2.3 符号连接操作符
##
作用:将宏定义的多个形参转换成一个实际参数名。
1 |
|
1.2.4 单字符化操作符
#@
将传入单字符参数名转换成字符,以一对单引用括起来。
1 |
|
条件表达式
空语句
1 | (c % 2 !=0) ? c *= 2 : **(void)**1; |