c语言里的宏(翻译) 6
原文在这里
连接
在宏展开过程中把两个符号连接在一起的特性往往会很有用。我们管这种特性叫符号粘贴或者符号连接。"##" 就是用于符号连接的预处理操作符。当一个宏展开后, "##" 两边的符号就会被组合成一个,然后该组合后的符号会替换"##"两边的符号以及"##"本身。通常情况下"##"两边的符号都会是标识符,或者一边是标识符,一边是预处理号。粘贴之后,一个更长的标识符就被生成了。这不是唯一有效的情况,把两个数字粘贴成一个数字也是可以的(比如1.5 或者 e3)。同样,多字符的操作符比如+=也可以通过粘贴生成。
但是,若两个符号粘贴后不是一个合法的字符,那么他们就不能被粘在一起。比如说,你不能把x和+粘在一起。如果你一定要这么做,预处理器会发出一个警告然后把两个符号都发射出来。符号两边是否需要有空格在标准里未定义。你经常可以在一些复杂的宏里发现一些没必要的"##"。如果编译器在这种情况下发出警告,一般来讲你可以简单的把"##"去掉就行。
"##"两边的符号都可以来自宏内容,这种情况下你也可以在一开始就把他们写成一个符号。最常见的使用"##"的场合是其中的一个符号或者两个符号都是宏参数。如果"##"两边的符号中有一个是宏参数,在"##"执行前,它会被实际内容替代。在字符化里,实际内容不会在宏展开前被替换。如果参数为空,"##"什么效果也没有。
有个事项必须要牢记:把注释替换成空格这件事,c语言的预处理器会在在做任何事情前完成。所以,你不能指望连接'/' 和 '*' 来生成一个注释。你可以在"##"两边放无数的空格,包括注释在内。你也可以在宏参数里写注释。但是,"##"出现在宏内容的两端是非法的。
考虑一段c代码用于解释命令。我们可能需要一个命令名表,或者一个包含一下结构内容的数组:
struct command { char *name; void (*function) (void); }; struct command commands[] = { { "quit", quit_command }, { "help", help_command }, ... };
每一个命令里,名字都出现了两次,一次在字符串里,一次在函数名里。这样的程序不够“干净”。一个把命令名当参数的宏可以有效的解决该类问题。字符常量可以用字符化生成,函数名可以用字符粘贴'_command’来生成。以下是完成后的代码
#define COMMAND(NAME) { #NAME, NAME ## _command } struct command commands[] = { COMMAND (quit), COMMAND (help), ... };