1. 语句表达式
GNU C 把包含在括号中的复合语句看做是一个表达式,称作语句表达式,它可以出现在任何允许表达式的地方。我们可以在语句表达式中使用原本只能在复合语句中使用的循环、局部变量等,例如:
#define min_t(type, x, y) \
({ type _x = (x); type _y = (y); _x < _y ? _x : _y; })
int ia, ib, mini;
float fa, fb, minf;
mini = min_t(int, ia, ib);
minf = min_t(float, fa, fb);
因为重新定义了 _x 和 __y 这两个局部变量,所以以上述方式定义人宏将不会有副作用。在标准 C 中,对应的如下宏则会产生副作用:
#define min(x, y) ((x) < (y) ? (x) : (y))
代码 min(++ia, ++ib) 会被展开为 ((++ia) < (++ib) ? (++ia) : (++ib)),传入宏的“参数”被增加 2 次。
2. typeof 关键字
typeof(x) 语句可以获得 x 的类型,因此,我们可以借助 typeof 重新定义 min 这个宏:
#define min(x, y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
我们不需要像 min_t(type, x, y) 这个宏那样把 type 传入,因为通过 typeof(x)、typeof(y) 可以获得 type。代码行 (void)(&_x == &_y) 的作用是检查 _x 和 _y 的类型是否一致。
3. 可变参数宏
标准 C 就支持可变参数函数,意味着函数的参数是不固定,例如 printf() 函数的原型为:
int printf(const char *format [, argument]...);
而在 GNU C 中,宏也可以接受可变数目的参数,例如:
#define per_debug(fmt, arg...) \
printk(fmt, ##arg)
这里 arg 表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构成 arg 的值,在宏扩展时替换 arg,例如下列代码:
pr_debug("%s : %d", filename, line)
会被扩展为:
printk("%s : %d", filename, line)
使用“##”的原因是处理 arg 不代表任何参数的情况,这时候,前面的逗号就变得多余了。使用“##”之后,GNU C 预处理会丢弃前面的逗号。
参考自:《Linux 设备驱动开发》