#, ##, args...以及__VAR_ARGS__宏定义解析

144 篇文章 13 订阅

在 linux kernel中,经常会看见类似的宏定义

点击(此处)折叠或打开

  1. #define printf(format, args...) \ 
  2.     printk(KERN_ERR "BFS-fs: %s(): " format, __FUNCTION__, ## args)


更有甚者,在 man printf时候,都会有类似

点击(此处)折叠或打开

  1. int printf(const char *format, ...);
  2.        int fprintf(FILE *stream, const char *format, ...);
  3.        int sprintf(char *str, const char *format, ...);
  4.        int snprintf(char *str, size_t size, const char *format, ...)


初识这些代码,对于其中:
1. " ##" 的含义和作用
2. args...作用
3. ...又是什么,省略号么?
都会有疑问,其作用,含义
同样是 C语言,为何之前学习 C时候,没有相关的介绍有涉及这些内容

对于 #, ##, args...以及 ...都是在预编译时候所使用和识别的,预编译结束后,这些标识都会被一一替换。
这也很好理解,谁让他们都是存在于 #define的代码中的呢

#
这个是用来提取生成字符串的

##
这个是用来连接前后两个(宏)变量的

比如对于

点击(此处)折叠或打开

  1. struct command
  2. {
  3. char *name;
  4. void (*function) (void);
  5. };
  6.  
  7. struct command commands[] =
  8. {
  9. { "quit", quit_command },
  10. { "help", help_command },
  11. ...
  12. }

可以使用如下宏来实现(使用 gcc -E test.c 查看预编译后生成的文件,效果是一样一样的),看起来会更加直观

点击(此处)折叠或打开

  1. #define COMMAND(NAME) { #NAME, NAME ## _command }
  2.  
  3. struct command commands[] =
  4. {
  5. COMMAND (quit),
  6. COMMAND (help),
  7. ...
  8. }

可见
#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 */类似的注释语句的

点击(此处)折叠或打开

  1. #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可能会有多个
个人感觉上,宏进行扩展时候,会以逗号作为标记来进行变量的区分和赋值动作
比如 

点击(此处)折叠或打开

  1. #define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## args)
  2.  
  3.     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字符串的赋值,可以使用上述的 #来实现,但是有些地方就会需要进行调整

点击(此处)折叠或打开

  1. #define MMarPrintf(format, args...) printf("==>Debug: %s-%d |" #format, __FUNCTION__, __LINE__, ## args) 
  2.  
  3. ...
  4.     MMarPrintf(Here sizeof(unVar): %d\n, sizeof(unVar));
  5.     //MMarPrintf(Here, sizeof(unVar): %d\n, sizeof(unVar))

会被赋值给 format的字串中,因为现在没有引号的作用,其中不能含有逗号,对于上述例子中,如果使用注释的第5句,在编译时候会提示错误

对于后续展开的宏中,是使用 ##args还是使用 args
1. 两者都可以实现可变宏变量的扩展
2. 但是使用 ##args会更有优势
具体原因在上述 ”##“讨论中的第5点中已经有所提及

点击(此处)折叠或打开

  1. #define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## args)
  2. //#define MarPrintf(format, args...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, args) 
  3.  
  4.     ...
  5.     MarPrintf("Here, sizeof(unVar)\n" );
  6.     MarPrintf("Here, sizeof(unVar): %d\n", sizeof(unVar));
  7.     MarPrintf("Here, sizeof(unVar): %d, %d\n", sizeof(unVar), sizeof(union UnTest))

如上,因为第5行中没有任何 args变量的存在,而只有 format变量,如果使用第2行的宏定义,在编译时候会提示错误。因为预编译后 第5行展开后将变成 

点击(此处)折叠或打开

  1. printf("==>Debug: %s-%d |" "Here, sizeof(unVar)\n", __FUNCTION__, 62, )

而如果使用第1行的宏定义,展开后的语句将是

点击(此处)折叠或打开

  1. printf("==>Debug: %s-%d |" "Here, sizeof(unVar)\n", __FUNCTION__, 62 )

末尾的逗号将会被取消。具体在何种情况下,该逗号会取消,可以参考如下 GNU的 Variadic Macros中的描述

__VAR_ARGS__和 ...
这两者是在 C99的标准中增加的功能,不同于上述的 args...(GNU的CPP中一贯都有支持)。
但是这两个功能是类似的,但是考虑到可移植性(对于不支持 C99的老版本而言),需要进行相应的转换动作,替换成 args...的形式

点击(此处)折叠或打开

  1. #define MarPrintf(format, ...) printf("==>Debug: %s-%d |" format, __FUNCTION__, __LINE__, ## /* 123 */ __VA_ARGS__) 
  2. void test_command(void)
  3. {
  4.     MarPrintf("Just for test!\n");
  5. }

具体用法和注意点,和上述的 args..都是类似的,只是格式有所不同而已

###########################参考内容############################
3.5 GNU ## Concatenation
3.6 GNU Variadic Macros
GNU宏定义中的 #args和 ##args

 

http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26527046&id=4923630

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值