在宏定义中#和##的作用是:前者将宏定义的变量转化为字符串;后者将其前后的两个宏定义中的两个变量无缝拼接在一起转化为字符串。
1. #: 在宏定义中,将其后的变量,转化为字符串。
eg: #define str(s) #s
输出: helloworld , 就可以通过这样的的调用, printf( str(helloworld) );
2. ## : 在宏定义中,将其前后的两个变量拼接在一起。
eg: #define v(a,b,c,d) 0xa##b##c##d
//将四个字节的十六进制的数据转化为一个十六进制的整型数据
int a = v(CF,AB,B0,BC); // 即 a = 0xCFABB0BC
3. 两者的共性: 能够阻止宏定义的递归展开。可以通过中间的转换的宏,来实现参数宏的展开。
测试例子:
#include
#include
#include
#define ROOT "/mnt/sd/"
#define NAME "kernel.img"
#define path(dir,name) dir##name
#define print(format,args...) printf(format,##args)
#define test(name) #name
enum
{
MON = 1,
TUE = 2,
SUN = 7
};
int main(int argc, char *argv[])
{
//printf("macro PATH = %s\n", PATH);
//printf("path(dir,name) = %s\n", path(ROOT,NAME));
printf("path(dir,name) = %s\n", path(RO,OT));
//printf("macro TEST = %s\n", TEST);
printf("test(name) = %s\n", test(NAME));
printf("test(MON) = %s\n", test(MON));
//printf("#MON = %s\n", #MON);
print("%s%s\n", ROOT,NAME);
return 0;
}
测试结果:
~/test$ ./macro_sign
path(dir,name) = /mnt/sd/
test(name) = NAME #宏不会递归展开
test(MON) = MON #可以很方便地实现枚举变量的字符串显示
/mnt/sd/kernel.img # ##符号的特殊用法
实际应用
使用#和##宏符号的作用定义最简单的日志接口,如下:
#define SLOG(level,format,args...) \
do{printf("[%s]",#level);printf(format,##args);}while(0)
SLOG(ERR,"%s",strerror(errno); // 调用方式,不需要定义ERR宏,接口会自动打印为字符串 这个宏可以很方便地替换在项目中使用的正式日志接口,用来保持模块的独立性,便于模块的测试。