在 linux kernel中,经常会看见类似的宏定义
-
#define printf(format, args...) \
-
printk(KERN_ERR "BFS-fs: %s(): " format, __FUNCTION__, ## args)
更有甚者,在 man printf时候,都会有类似
-
int printf(const char *format, ...);
-
int fprintf(FILE *stream, const char *format, ...);
-
int sprintf(char *str, const char *format, ...);
-
int snprintf(char *str, size_t size, const char *format, ...)
初识这些代码,对于其中:
1. " ##" 的含义和作用
2. args...作用
3. ...又是什么,省略号么?
都会有疑问,其作用,含义
同样是 C语言,为何之前学习 C时候,没有相关的介绍有涉及这些内容
对于 #, ##, args...以及 ...都是在预编译时候所使用和识别的,预编译结束后,这些标识都会被一一替换。
这也很好理解,谁让他们都是存在于 #define的代码中的呢
#
这个是用来提取生成字符串的
##
这个是用来连接前后两个(宏)变量的
比如对于
-
struct command
-
{
-
char *name;
-
void (*function) (void);
-
};
-
-
struct command commands[] =
-
{
-
{ "quit", quit_command },
-
{ "help", help_command },
-
...
-
}
可以使用如下宏来实现(使用 gcc -E test.c 查看预编译后生成的文件,效果是一样一样的),看起来会更加直观
-
#define COMMAND(NAME) { #NAME, NAME ## _command }
-
-
struct command commands[] =
-
{
-
COMMAND (quit),
-
COMMAND (help),
-
...
-
}
可见
#NAME 将生成 "NAME"的字符串。如果 NAME是宏变量,会在 #起作用前先行展开
NAME ## _command 将会连接前后两个记号(token),生成一个新的记号(token) NAME_command。 当然和上面的类似,如果NAME作为宏变量,在 ##连接前会先行展开
对于 ##,需要补充如下两点:
1. ##连接符生成的新记号(token),应该是一个合法的,有意义的记号。比如你不能连接 "c"和 "-"/ "+"等等类似标记来生成"c-"或者 "c+",否者预编译时候就会提示错误
2. ##连接生成的新记号(虽然已经通过预编译),但是如果生成的是某变量,依旧需要确保该变量名是合法的。比如"c"和 "_-var"虽然可以使用##来进行连接而生成"c_-var"(预编译可以正常完成),但是在后续的编译环节依旧会报错(当然这个是后话,严格来说不属于 ##的预编译讨论范畴)(判断规范,可以参照C语言变量定义)
3. 所有的注释代码,在 ##进行连接前,预编译器已经将他们翻译成空格符了,因此不能期望使用 ##来连接 /和 *来生成一个注释代码(当然如果不是闲得疼,应该也不会这么用吧?)
4. ##和前后两个记号之间的空格数可以随意;并且因为以上第3点说明,##和记号间是可以插入 /* XXX */类似的注释语句的
-
#define MarPrintf(format, ...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## /* 123 */ __VA_ARGS__)
5. 在最上面的例子的 ##args的使用中,##的作用不是连接 format和 args。而是实现:
" 如果宏扩展时候发现 args不存在时候(也就是只有 format,但是没有 args),"##"配置前面紧邻的 ","(也就是 format后的逗号)来实现删除 format后面的逗号的作用(具体实例说明可以参考下面的描述)
args...
这个是 GNU CPP中对于可变的宏变量所支持的用法,表示后续的 args可能会有多个
个人感觉上,宏进行扩展时候,会以逗号作为标记来进行变量的区分和赋值动作
比如
-
#define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## args)
-
-
MarPrintf("Here, sizeof(unVar): %d, %d\n", sizeof(unVar), sizeof(union UnTest))
其中
"Here, sizeof(unVar): %d, %d\n"赋值给 format。注意连同引号一起都会赋值给 format
sizeof(unVar
), sizeof(union UnTest
)会赋值给 args...
当然,对于 format字符串的赋值,可以使用上述的 #来实现,但是有些地方就会需要进行调整
-
#define MMarPrintf(format, args...) printf("==>Debug: %s-%d |" #format, __FUNCTION__, __LINE__, ## args)
-
-
...
-
MMarPrintf(Here sizeof(unVar): %d\n, sizeof(unVar));
-
//MMarPrintf(Here, sizeof(unVar): %d\n, sizeof(unVar))
会被赋值给 format的字串中,因为现在没有引号的作用,其中不能含有逗号,对于上述例子中,如果使用注释的第5句,在编译时候会提示错误
对于后续展开的宏中,是使用 ##args还是使用 args
1. 两者都可以实现可变宏变量的扩展
2. 但是使用 ##args会更有优势
具体原因在上述 ”##“讨论中的第5点中已经有所提及
-
#define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## args)
-
//#define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, args)
-
-
...
-
MarPrintf("Here, sizeof(unVar)\n" );
-
MarPrintf("Here, sizeof(unVar): %d\n", sizeof(unVar));
-
MarPrintf("Here, sizeof(unVar): %d, %d\n", sizeof(unVar), sizeof(union UnTest))
如上,因为第5行中没有任何 args变量的存在,而只有 format变量,如果使用第2行的宏定义,在编译时候会提示错误。因为预编译后 第5行展开后将变成
-
printf("==>Debug: %s-%d |" "Here, sizeof(unVar)\n", __FUNCTION__, 62, )
而如果使用第1行的宏定义,展开后的语句将是
-
printf("==>Debug: %s-%d |" "Here, sizeof(unVar)\n", __FUNCTION__, 62 )
末尾的逗号将会被取消。具体在何种情况下,该逗号会取消,可以参考如下 GNU的 Variadic Macros中的描述
__VAR_ARGS__和 ...
这两者是在 C99的标准中增加的功能,不同于上述的 args...(GNU的CPP中一贯都有支持)。
但是这两个功能是类似的,但是考虑到可移植性(对于不支持 C99的老版本而言),需要进行相应的转换动作,替换成 args...的形式
-
#define MarPrintf(format, ...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## /* 123 */ __VA_ARGS__)
-
void test_command(void)
-
{
-
MarPrintf("Just for test!\n");
-
}
具体用法和注意点,和上述的 args..都是类似的,只是格式有所不同而已
###########################参考内容############################
GNU宏定义中的 #args和 ##args
阅读(2070) | 评论(0) | 转发(0) |