computer:c:tips:macro_with_parameters
参数宏(Macro with parameters)
括号规则
对于带参数的宏,定义时左括号(parenthesis)必须紧接着宏名,之间不能有空格,否则,从左括号开始的部分都被识别为宏的定义。当运行时,宏名和左括号之间可以有空白。
宏调用时,实参可以包含多层括号,括号内可以有逗号(commas)。大括号(braces)和中括号(brackets)也可以出现在实参里,但他们里面不能包含逗号。
例如定义:
#define insert(stmt) stmt
如下调用是合法的:
insert( { a = 1; b = 1; } )
但这个就不行:
insert( { a = 1, b = 1; } )
必须写成这样才行:
insert( { (a = 1, b = 1); } )
有点诡异,但这是语法的要求。
宏的本质
编写带参数的宏需要特别小心,因为宏扩展的本质仅仅是进行文本式的替换(textual substitution),它不是真正的函数。
例如:
#define SQUARE(x) x * x
本意是求平方,但调用:
SQUARE(z + 1)
的扩展结果却是:
z + 1 * z + 1
一个安全的做法是在宏体内使用括号括住每个参数。如果整个宏的形式是一个表达式,那么宏也用括号括住。
上例改造后:
#define SQUARE(x) ((x) * (x))
如果没有用括号括住整个宏,那么下面的调用仍旧可能会出问题:
(short)SQUARE(z + 1)
因为强制转换的优先级更高。
宏的副作用
括号不是万能的,还要注意宏参数可能存在的副作用,毕竟宏不是函数。
例如:
a = 3; b = SQUARE(a++);
导致a++了两次,结果为5,而b的结果则依赖于实现。如果SQUARE是个函数,就不会有这个问题。
do-while模式
对于多语句的情况,一般用do-while结构比较合适。
比如下面的实现:
#define swap(x, y) {unsigned long temp = x; x = y; y = temp;}
实际调用时,很容易在末尾加个分号(semicolon),导致编译出错:
if ( x > y) swap(x, y); else x = y;
改用do-while实现就好了:
#define swap(x, y) \ do { unsigned long temp = x; x = y; y = temp; } while (0)
变参宏
C99允许在参数中使用...实现变参宏。所有额外的参数,包含其中的逗号,用标识符__VA_ARGS__代替。
比如:
#define my_printf(...) fprintf(stderr, __VA_ARGS__)
调用:
my_printf("x = %d\n", x);
的扩展结果为:
fprintf(stderr, "x = %d\n", x);
另一个例子:
#define make_em_a_string(...) #__VA_ARGS__
调用:
printf("%s\n", make_em_a_string(a, b, c, d));
的扩展结果为:
printf("%s\n", "a, b, c, d");
computer/c/tips/macro_with_parameters.txt · 最后更改: 2014/11/01 02:02 由 127.0.0.1