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"
 
posted @ 2008-10-20 19:09  gussing  阅读(855)  评论(0编辑  收藏  举报