#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。(中间可以有空格或Tab?)
假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).#在字符串中使用时,需要用双引号引起来,否则会当成普通的字符串进行处理。
##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。
##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。
另外,如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开 。
宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错.
##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。
函数式宏定义和真正的函数调用有什么不同:
1、函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。
2、调用真正函数的代码和调用函数式宏定义的代码编译生成的指令不同。
C标准库的很多函数都提供两种实现,一种是真正的函数实现,一种是宏定义实现。
C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简单的字符串替换,根据是否带参数分为无参和带参。
使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,
从而不会与上下文发生混淆,避免分号问题。do...while的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。
通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C语言调用一样,通常情况下我们在宏的后面加上一个分号
先进行# stringified操作,再对参数进行替换, 最后执行## pasted 操作。
Macro parameters appearing inside string literals are not replaced by their corresponding actual arguments.
来源:https://www.cnblogs.com/yanggongfu/p/3832626.html