看看以下两个宏定义:

#define PSQR(x) printf("The square of x is %d.
",(x)*(x))

#define PSQR(x) printf("The square of "#x" is %d.
",(x)*(x))

    我们的目标是希望使用宏的时候将宏体中的x替换为宏参数,但是第一个宏定义是不能完成这样的目标的,因为引号中的x会被看做是普通文本,不可替换;我们可以采用#运算符来实现这样的功能。

 

PSQR(x)在两个不同的宏定义输出的结果如下:

The square of x is 4.

The square of 2 is 4.

 

define宏定义中的#,##,@#及符号  


来源不详,seclu整理
1、# (stringizing)字符串化操作符。其作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。
如:
    #define example(instr) printf("the input string is: %s ",#instr)
    #define example1(instr) #instr
当使用该宏定义时:
example(abc); 在编译时将会展开成:printf("the input string is: %s ","abc");
string str=example1(abc); 将会展成:string str="abc";
注意:
对空格的处理
a.忽略传入参数名前面和后面的空格。
   如:str=example1(   abc ); 将会被扩展成 str="abc";
b.当传入参数名间存在空格时,编译器将会自动连接各个子字符串,用每个子字符串中只以一个空格连接,忽略其中多余一个的空格。
   如:str=exapme( abc    def); 将会被扩展成 str="abc def";
 
2、## (token-pasting)符号连接操作符
宏定义中:参数名,即为形参,如#define sum(a,b) (a+b);中a和b均为某一参数的代表符号,即形式参数。
而##的作用则是将宏定义的多个形参成一个实际参数名。
如:
    #define exampleNum(n) num##n
    int num9=9;
使用:
int num=exampleNum(9); 将会扩展成 int num=num9;
注意:
1.当用##连接形参时,##前后的空格可有可无。
如:#define exampleNum(n) num ## n 相当于 #define exampleNum(n) num##n
2.连接后的实际参数名,必须为实际存在的参数名或是编译器已知的宏定义
    // preprocessor_token_pasting.cpp
    #include <stdio.h>
    #define paster( n ) printf_s( "token" #n " = %d", token##n )
    int token9 = 9; 
    int main() 
    { 
       paster(9); 
    }
运行结果:
token9 = 9
另外,如果##后的参数本身也是一个宏的话,##会阻止这个宏的展开。
  #define STRCPY(a, b)    strcpy(a ## _p, #b)
  int main() 
  { 
      char var1_p[20]; 
      char var2_p[30]; 
      strcpy(var1_p, "aaaa"); 
      strcpy(var2_p, "bbbb"); 
      STRCPY(var1, var2); 
      STRCPY(var2, var1); 
      printf("var1 = %s ", var1_p); 
      printf("var2 = %s ", var2_p); 
      return 0; 
      
      STRCPY(STRCPY(var1,var2),var2); 
      
  }
 
3、@# (charizing)字符化操作符。
只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。作用是将传的单字符参数名转换成字符,以一对单引用括起来。
    #define makechar(x)  #@x
    a = makechar(b);
展开后变成了:
a= 'b';
4、 行继续操作符
当定义的宏不能用一行表达完整时,可以用""表示下一行继续此宏的定义。


另:关于其他网友对##和#的补充
1. 简单的说,“##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。
   其中,分隔的作用类似于空格。我们知道在普通的宏定义中,预处理器一般把空格
   解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,
   被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些
   ##来替代空格。
   另外一些分隔标志是,包括操作符,比如 +, -, *, /, [,], ...,所以尽管下面的
   宏定义没有空格,但是依然表达有意义的定义: define add(a, b)  a+b
   而其强制连接的作用是,去掉和前面的字符串之间的空格,而把两者连接起来。
 
2. 举列 -- 试比较下述几个宏定义的区别
   #define A1(name, type)  type name_##type##_type //或 
   #define A2(name, type)  type name##_##type##_type
   A1(a1, int);  
   A2(a1, int);    
解释:
        1) 在第一个宏定义中,"name"和第一个"_"之间,以及第2个"_"和第二个
   "type"之间没有被分隔,所以预处理器会把name_##type##_type解释成3段:
   “name_”、“type”、以及“_type”,这中间只有“type”是在宏前面出现过
    的,所以它可以被宏替换。
        2) 而在第二个宏定义中,“name”和第一个“_”之间也被分隔了,所以
   预处理器会把name##_##type##_type解释成4段:“name”、“_”、“type”
   以及“_type”,这其间,就有两个可以被宏替换了。
        3) A1和A2的定义也可以如下:
         #define A1(name, type)  type name_  ##type ##_type
         //<##前面随意加上一些空格>
         #define A2(name, type)  type name ##_ ##type ##_type
结果是## 会把前面的空格去掉完成强连接,得到和上面结果相同的宏定义
3. 其他相关 -- 单独的一个 #
   至于单独一个#,则表示 对这个变量替换后,再加双引号引起来。比如
#define  __stringify_1(x)   #x
那么
      __stringify_1(linux)   <==>  "linux"
所以,对于MODULE_DEVICE_TABLE
    #define MODULE_DEVICE_TABLE(type,name)
        MODULE_GENERIC_TABLE(type##_device,name) 
    #define MODULE_GENERIC_TABLE(gtype,name)
        extern const struct gtype##_id __mod_##gtype##_table
        __attribute__ ((unused, alias(__stringify(name))))
得到
      MODULE_DEVICE_TABLE(usb, products)
                            
    <==> MODULE_GENERIC_TABLE(usb_device,products)
    <==> extern const struct usb_device_id __mod_usb_device_table
             __attribute__ ((unused, alias("products")))
注意到alias attribute需要一个双引号,所以在这里使用了__stringify(name)来
给name加上双引号。另外,还注意到一个外部变量"__mod_usb_device_table"被alias
到了本驱动专用的由用户自定义的变量products<usb_device_id类型>。这个外部变量
是如何使用的,更多的信息请参看《probe()过程分析》。
4. 分析方法和验证方式 -- 编写一个简单的C程序
   用宏定义一个变量,同时用直接方式定义一个相同的变量,编译报告重复定义;
   用宏定义一个变量,直接使用该宏定义的变量名称,编译通过且运行结果正确;
   使用printf打印字符串数据。printf("token macro is %s", __stringify_1(a1));