看看以下两个宏定义:

#define PSQR(x) printf("The square of x is %d.\n",(x)*(x))#define PSQR(x) printf("The square of "#x" is %d.\n",(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:\t%s\n",#instr) #define example1(instr) #instr当使用该宏定义时:example(abc); 在编译时将会展开成:printf("the input string is:\t%s\n","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##n2.连接后的实际参数名,必须为实际存在的参数名或是编译器已知的宏定义 // 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\n", var1_p); printf("var2 = %s\n", 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));

看着书里九万五千公里的绚丽。又或是和我一样,

宏定义 宏参数