原文译至:http://complete-concrete-concise.com/programming/c/preprocessor-the-token-pasting-operator
##可能是最不为人所知,也最缺少文档的预处理器操作符了。
记号传递操作符(##
) 仅仅删除它周围的所有空格和将非空的字符连接到一起。它只能用于一个宏定义中,它被用于创建新的记号。
它不能是替换文本里的第一个或最后一个字符。
如果有多个记号传递操作符(##
)和/或字符串化操作符(#)那么处理的顺序是不定的。
有效的记号是:
如果使用##的不是有效的记号,那么行为是未定义的。
考虑一下如下的宏定义:使用##操作符来在替换字符串中移除额外的空格。
#define macro_start i ## n ##t m ##ain(void)
在预处理后macro_start 名将会被替换成:
int main(void)
虽然记号传递操作符可用于无参数的宏,但是这没有什么意义。因为你可以无须##直接打印你需要的东西。
##只是在你用它来连接你传递到宏中的参数时才发挥它的威力(只要是关于预处理器的,它处理的所有东西就是一堆文本).
通常情况下,它用于自动创建新的标志符。例如:
#define my_macro(x) x##_macrofied
x
) 并追加后缀
_macrofied到x后面
.
my_macro(identifier)
identifier_macrofied
GCC 和Visual C++按不同的方式处理 ##
.
如果连接的结果不是一个有效的预处理记号,GCC 的处理是严格的 – 它在编译阶段会产生一个错误。
Visual C++, 在另一方面,重新处理了连接的结果并支持它,而它在GCC中被视为无效的。
这两个编译器的处理都可以正常工作,因为标准并没有定义一个无效的记号就如何被处理的(它只是说行为是未定义的)。拒绝它好点因为这重新处理了结果并被解析成有效的记号。
下面的例子在GCC中失败但是Visual C++里成功:
#define macro_start int main ## (void)
main(
这不是一个有效的记号。
GCC 不支持它。
另一方面,Visual C++ 重新处理来生成两个记号: 1) 一个标志符 main
和 2) 标点 / 操作符(。
两个编译器都能正常处理以下的宏
#define macro_increment(x) x+ ## +
它主要是用于降低重复的(易于出错)输入。
下面的代码定义了一个宏来创建一个C/C++中的新标量类型,并创建了6个针对那个类型的名字定制的函数。(初始化,加,减,乘,除和原始值访问)。不使用##操作符的话,这就要手工输入(如果你定义许多类型的话,这将重复操作,令人厌烦,而且容易出错)或是通过拷贝/粘贴/查找和替换操作(仍旧是重复操作,令人厌烦,而且容易出错)。
#define new_scalar_type(name, type) \ typedef struct \ { \ type value; \ } name; \ inline name name##_(type v) \ { \ name t; \ t.value = v; \ return t; \ } \ inline name add_##name(name a, name b) \ { \ name t; \ t.value = a.value + b.value; \ return t; \ } \ inline name sub_##name(name a, name b) \ { \ name t; \ t.value = a.value - b.value; \ return t; \ } \ inline name mul_##name(name a, name b) \ { \ name t; \ t.value = a.value * b.value; \ return t; \ } \ inline name div_##name(name a, name b) \ { \ name t; \ t.value = a.value / b.value; \ return t; \ } \ inline type value_##name(name a) \ { \ return a.value; \ } \
当你在你的代码里使用宏:
new_scalar_type(age, int);
一个初始化函数age age_(int t)
age add_age(age a, age b)
age sub_age(age a, age b)
age mul_age(age a, age b)
age div_age(age a, age b)
int value_age(age a)
如果你定义了一个新类型,叫做 weight_lbs
, 那么你将会得到新的类型和相关的定制的命名函数,你就可以操作这些函数。
你也能定义一个新类型,按下面的方式使用:
new_scalar_type(age, int); int main (void) { age a = age_(42); age b = age_(24); age c = add_age(a, b); return value_age(c); }
下面的例子(被称作convert,能被用于自动生成一个将一个值转换成另一个值的函数 (例如, °F 到 °C 或英尺到英寸,等等):
#define convert(from, to, conversion, from_type, to_type) \ to_type convert_##from##_to_##to(from_type f) \ { \ return conversion; \ } \
它用到5个参数:
from
- a descriptive name of the unit we are converting fromto
- a descriptive name of the unit we are converting toconversion
- the conversion equation (yes, macro parameters can be complex)from_type
- the type we are converting fromto_type
- the type we are converting to该宏可以这么使用:
convert(f, c, (f-32)*5.0/9.0, float, float); convert(ft, in, ft * 12, int, int);
当宏被扩展时,我们得到两个叫做 convert_f_to_c
和 convert_ft_to_in的函数。这些可以按下面的方法使用:
int main (void) { float a = 70.0; float b; int c = 3; int d; b = convert_f_to_c (a); d = convert_ft_to_in(c); return 0; }