宏的细节 形式参数 形参是个有效的 C 标识符, 以逗号和可选的空格分割。 The parameters must be valid C identifiers, separated by commas and optionally whitespace. 实际参数 实参是以逗号和可选的空格分割。这导致了宏的一个缺陷,参数不能是 (a,b) 这样的,boost的foreach宏就受到这个限制。 gcc不受这个限制 The arguments is separated by commas and optionally whitespace. 例如: #define CALL(f,a) f a CALL(printf, ( "%d" , 3 ) ) ==> printf ( "%d" , 3 ) Stringified #和##只在宏定义中有效。# stringified 把字符# 右边的 宏参数 转换为字符串 "argument"example:#define str(a) #a str(ADD(x)) ==> "ADD(x)" Pasted ## pasted 对宏进行参数替换后,去除字符##, 这样就可以实现token合并example:#define A abc##def A ==> abcdef macro body 展开过程 先进行# stringified操作,再对参数进行替换, 最后执行## pasted 操作。 Simple scan 和 Twice scan object-like宏 和 function-like但没有参数的宏,或macro body 有 #(stringified ) or ##(pasted) 的macro, 只执行一遍扫描(simple scan)。否则就要执行两遍扫描。 两篇扫描:prescan: 对参数进行扫描,并对可以展开的参数进行完全的宏展开。second scan: 用展开后的参数,对宏体进行展开,对展开后的结果 递归进行 完全的宏展开。 simple scan 执行 second scan 一样的过程。 example: simple sacn: #define no_param hah no_param ==> hah #define UNAME(a) a##__LINE__ UNAME(lidy) ==> lidy__LINE__ #define INC(x) x+1 #define STR(b) #b STR(INC(x)) ===> "INC(x)" 递归问题 无论是simple scan 或 twice scan的宏展开过程,都不允许对同一宏进行第二次展开。 example:simple scan: #define x (4 + y) #define y (2 * x) x ==> (4 + y) ==> (4 + (2 * x)) twice scan: #define a(x) a(x)+1 #define b(x) x+2 b(a(y)) ==>b(a(y)+1) ==>a(y)+1 +2 可变参数的宏 用__VA_ARGS__ 引用可变参数: #define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__) eprintf("abc:%d", 3) ===> fprintf(stderr,"abc:%d", 3) eprintf("bad") ===> fprintf(stderr,"abc:%d", ) 出错,参数太少了 使用##__VA_ARGS___ 可以处理0参数的情形#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__) eprintf("abc:%d", 3) ===> fprintf(stderr,"abc:%d", 3) eprintf("bad") ===> fprintf(stderr,"abc:%d") 使用其它名字#define eprintf(format, args...) fprintf (stderr, format , ##args) 宏不允许重复定义 如果两个宏定义基本一致,是不会报错的。 判断宏一致:4条都要满足1同是object-or function-like2 宏体中的token要相同(就是空白分割的token)3 如果有参数,那么形参要相同4 有相同的空白处(空白字符数不要求一样和像HTML那样) 相同定义,不报错 #define FOUR (2 + 2) #define FOUR (2 + 2) #define FOUR (2 /* two */ + 2) 重复定义错误: #define FOUR (2 + 2) #define FOUR ( 2+2 ) #define FOUR ( 2 + 2) //第4条 空白处不对 #define FOUR (2 * 2) //第3条 宏体的TOKEN不同 #define FOUR(score,and,seven,years,ago) (2 + 2) //第一条不符号 宏调用中使用宏指令 Directives Within Macro ArgumentsIf, within a macro invocation, that macro is redefined, then the new definition takes effect in time for argument pre-expansion, but the original definition is still used for argument replacement.宏调用中重定义那个宏,那么新的定义只在参数展开中起作用,外层宏的宏体展开还是使用原先的定义 #define f(x) x x f (1 #undef f #define f 2 f) ==>1 2 1 2 f(2 3) ==> 2(2 3) 多行调用一个宏: Here is an example illustrating this: #define ignore_second_arg(a,b,c) a; c ignore_second_arg (foo (), ignored (), syntax error); ==> foo (); syntax error 这会导致程序的序号提示错误。 对于宏调用尽量都在一行内完成。 ignore_second_arg (foo (),ignored (), syntax error); typeof扩展和 embeded statement ({})表达式 减少重复计算,可以使用 gcc的 typeof扩展和 语句表达式({...}) 对语句进行计算,位于括号中的复合语句的最后一句必需是一个以分号结尾的表达式,它的值将成为这个语句表达式的值。 #define min(X, Y) \ ({ typeof (X) x_ = (X); \ typeof (Y) y_ = (Y); \ (x_ < y_) ? x_ : y_; }) 简单语句模拟 对于多个语法行的宏定义,建议使用do {...} while (0) 来包裹, 这可以把宏调用当成是一个简单的语句 #define SKIP_SPACES(p, limit) \ { char *lim = (limit); \ while (p < lim) { \ if (*p++ != ' ') { \ p--; break; }}} if (*p != 0) SKIP_SPACES (p, lim); else ... 将出错。 好的方式是: #define SKIP_SPACES(p, limit) \ do { char *lim = (limit); \ while (p < lim) { \ if (*p++ != ' ') { \ p--; break; }}} \ while (0) 下面是一个对宏进行展开的宏: #define TO_STRING(x) TO_STRING1(x)#define TO_STRING1(x) #x 原理也比较简单,第一次扫描对参数进行完全的展开,然后遇到 # 号即停止,输出展开结果