Clang 宏定义初探(二)

来源:互联网 发布:渠道营销 知乎 编辑:程序博客网 时间:2024/05/16 13:03

Clang 宏定义初探(二)

本篇总结下这几天看的宏的一些看到的用法。

1、参数粘结

这是一个类似 shell 之类的脚本语言的特性,可以利用这个特性完成一些重复度比较高的编码的简化。
例如,对proc文件系统进行绑定的时候,需要在/proc/test/目录下,简历3个文件接口,test1、test2、test3.
可以这样写

#define BIND(x) test##x->read_proc=test##x##_read

在使用的时候,就可以

BIND(1); //展开为test1->read_proc=test1_read;BIND(2); //展开为test2->read_proc=test2_read;

不管是从语义还是编码复杂度,都降低了。

2、参数字符化

在使用单个 # 号,作为函数式宏的参数前缀时,可以让宏的内容变成字符串,比如说:

#define print(x) do{\printf(#x);\printf("=%d\n",x);\}while(0)

使用的时候,直接写:

int t = 1;print(t);

结果会是 t=1,这个在做日志的时候还是非常好用的。

3、do{…}while(0) 和 ({…})

可以认为是前者是 void 函数,后者是有 return 值的函数。
入2中所示,do{…}while(0) 是为了产生一个程序块,当宏里有多条需要语句需要执行时,如果不适用这种do{…}while(0)的形式,可能导致一些隐形的错误,例如:

#define print(x) {printf(#x);printf("=%d\n",x);

正常的:
print(t); 是没有问题的,但是如果放在程序段里:

if( flag )  print(t);else  print(a);

展开之后,会发现为

if( flag ){printf("t");printf("=%d\n",t);};else...

这个语法就错了。因此,当代码段比较多,且不需要返回值时就用 do{…}while(0) 吧。
另外一种方式属于 GNU 的扩展,后续在看。

4、多重展开

还是基于打印的例子,我需要打印一些列举的参数值:

#define P(x) arg##x#define print(x) do{printf(#P(x));printf("=%d\n",P(x));}while(0)

这个编译通不过,换成以下方式即可:

#define P(x) arg##x#define __print(x) do{printf(#x);printf("=%d\n",x);}while(0)#define _print(x) __print(x)#define print(x) _print(P(x))

修改成这样,解决了想要的解决的问题:

int arg1 = 1;print(1);

输出结果为:arg1=1
主要涉及的问题在于宏的多次展开,宏每次展开只会对当前的输入参数进行一次展开,当你的输入值也是个宏的时候,就需要使用过度宏,让你的输入接着展开。
对于多次展开没有从最根本的原理解释,只是从实验感官上对这个特性做了分析,实际上,自己也不会写出那么复杂的宏(怕中间调用出漏洞)。

宏的基本常见用法,都差不多枚举了一番,往后在见到更高级的玩法和比较精髓的写法往后再慢慢补充上来吧,另外GNU的扩展也会在后篇继续学习了解。

0 0