博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
#, ##, args...以及__VAR_ARGS__宏定义解析
阅读量:4222 次
发布时间:2019-05-26

本文共 3807 字,大约阅读时间需要 12 分钟。

在 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..都是类似的,只是格式有所不同而已

###########################参考内容############################

 

转载地址:http://ltemi.baihongyu.com/

你可能感兴趣的文章
使用Beyond Compare对比文件夹
查看>>
深入理解java虚拟机 -- jVM高级特性与最佳实践
查看>>
阿里巴巴 java 开发规约
查看>>
impdp命令出现ora-39070解决方案
查看>>
ora-01756
查看>>
java 核心技术Ⅱ--章四:网络
查看>>
java 核心技术Ⅱ--章五:JDBC数据库编程
查看>>
java 核心技术Ⅱ--章六:时间与日期API
查看>>
链表,循环链表,双向链表,判环和入环点
查看>>
浅谈HashMap,HashTable,ConcurrentHashMap,WeakHashMap,HashMap源码分析
查看>>
云创大数据校企合作项目斩获“全国校企合作十佳案例”
查看>>
云创大数据与宽泛科技签订战略合作协议
查看>>
免费!免费!免费!全国高校大数据师资实战免费培训班
查看>>
“南京市独角兽瞪羚企业俱乐部创始人简餐会”走进云创
查看>>
本科大数据专业该怎么上?
查看>>
云创大数据1+X大数据应用部署与调优职业技能等级证书预申报正式开启!
查看>>
人工智能需要一个可被证明的理论作为基础 | 哈佛丘成桐
查看>>
入门 | 一文概览深度学习中的激活函数
查看>>
一分钟整明白Tensorflow Extended
查看>>
人工智能再次参加高考:和作家比写作文,AI能打多少分?
查看>>