c语言里的宏(翻译)4
原文在这里
宏参数
function-like宏可以带参数,就好像函数带参数一样。定一个一个带参数宏的时候,把参数插入到两个括号之间,就好像定义函数的参数一样。这就是该类宏被称为function-like宏的原因。宏参数必须是合法的C标识,由逗号和空格隔开。
调用带参数的宏,你可以在写完宏名之后插一对括号,然后在括号里跟实参,由逗号隔开。宏调用的代码并不是必须写在一行里,你可以想写多少行就写多少行。实参的数目必须符号定义时参数的数目。宏展开时,宏内容里的参数会自动被实参内容替代。(并不是所有的参数都必须在宏内容里出现。)
举个例子,这里有一个宏,作用是计算两个数值中较小的那一个,c语言的代码里常出现这样的宏
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) x = min(a, b); ==> x = ((a) < (b) ? (a) : (b)); y = min(1, 2); ==> y = ((1) < (2) ? (1) : (2)); z = min(a + 28, *p); ==> z = ((a + 28) < (*p) ? (a + 28) : (*p));
(在这个小宏里面,你已经可以看到很多带参数宏的危险,详情请看宏的陷阱)
起始的和末尾的空格会被虑掉,两个记号之间的所有空格都会被替换成一个空格。每个参数间的括号必须配对;括号内的逗号不会结束语句。然而,中括号和大括号则不必配对,并且中括号或大括号内的逗号会被当成是分割两个参数的记号,所以
macro (array[x = y, x + 1])
解析出了两个参数:array[x = y, 以及x + 1]。如果你想要的结果是把array[x=y, x+1]整个当成参数,那么你就得这样写:array[(x=y, x+1)]。
所有的宏参数都在替换到宏内容之前展开。替换了之后,完整的内容又被扫描一遍,以便宏内容包括参数本身被展开。这条规则看上去很奇怪,但它是仔细设计过的,这么做的结果就是,你完全不必关心你调用的到底是函数还是宏。实际上如果你想在这点上表现的聪明点,结果反而会很糟。参看参数预扫描获取更多的细节。
举个例子,min (min (a, b), c)
首先被展开成
min (((a) < (b) ? (a) : (b)), (c))
然后展开成
((((a) < (b) ? (a) : (b))) < (c) ? (((a) < (b) ? (a) : (b))) : (c))
(这里的分行只是为了看着方便,不是真的会展开成分行的代码)
你可以把宏参数留空,预处理器不会认为这是个错误(但是很多宏会展开成不合法代码)。但是你不能完全不管参数。如果一个宏带两个参数,参数列表的顶层必须只能有一个逗号
min(, b) ==> (( ) < (b) ? ( ) : (b)) min(a, ) ==> ((a ) < ( ) ? (a ) : ( )) min(,) ==> (( ) < ( ) ? ( ) : ( )) min((,),) ==> (((,)) < ( ) ? ((,)) : ( )) min() 错误,min宏要求两个参数,但这里只有一个参数
min(,,) 错误,min宏里面传入了3个参数,但实际只需要两个
空格不是预处理符号,所以如果宏foo带一个参数,foo ()和foo ( )都提供给宏一个空参数。之前的GNU预处理器对这一点的实现和文档都是错误的。
字符常量里出现的宏参数名不会展开:
#define foo(x) x, "x"foo(bar) ==> bar, "x"