--事物的难度远远低于对事物的恐惧!
这个篇章我们来谈一谈#和##操作符,可能很多人都没注意这两个操作符,然而这两个操作符,往往能给我们开发提供很大的便利。
#运算符:
-#运算符用于在预处理期将宏参数转换为字符串
-#的转换作用是在预处理期完成的,因此只在宏定义中有效
-编译器不知道#的转换作用
用法如下:#define STRING(x) #x
下边来看看代码
#define STRING(x) #x int main() { char *p = STRING(abc); return 0; }
预编译后的文件内容如下,我们可以看到,我们所定义的宏STRING(abc),在经过预处理器处理后,成为了"abc"字符串。
#line 1 "main.c" int main() { char *p = "abc"; return 0; }
知道了#运算符的基本用法,我们来看看#运算符在工程中比较巧妙的使用方式:
#include <stdio.h> //通过#及逗号表达式,使得我们使用宏的时候,既能打印出调用的函数名,又能执行函数 #define CALL(f, p) (printf("Call function %s\n", #f), f(p)) int square(int n) { return n * n; } int func(int x) { return x; } int main() { int result = 0; result = CALL(square, 4); printf("result = %d\n", result); result = CALL(func, 10); printf("result = %d\n", result); return 0; }
编译执行结果如下,是不是很巧妙:
##运算符:
-##运算符用于在预处理期粘连两个标识符
-##的连接作用是在预处理期完成的,因此只在宏定义中有效
-编译器不知道##的连接作用
用法:#define CONNECT(a,b) a##b
来看一个例子:
#define NAME(n) name##n int main() { int NAME(1); int NAME(2); NAME(1) = 1; NAME(2) = 2; return 0; }
使用预处理器处理后的文件内容为:
#line 1 "main.c" int main() { int name1; int name2; name1 = 1; name2 = 2; return 0; }
从预处理文件看出,##运算符已经把name与n粘结在一起了。
下边再来看看工程中典型的用法:
#define STRUCT(type) typedef struct _tag_##type type;\ struct _tag_##type STRUCT(Student) //使用宏STRUCT来定义名为Student的结构体,后边可直接使用Student声明定义结构体变量 { char* name; int id; }; int main() { Student s1; Student s2; s1.name = "s1"; s1.id = 0; s2.name = "s2"; s2.id = 1; return 0; }
我们用预处理器来处理下,得到的文件内容如下:
#line 1 "main.c" typedef struct _tag_Student Student; struct _tag_Student { char* name; int id; }; int main() { Student s1; Student s2; s1.name = "s1"; s1.id = 0; s2.name = "s2"; s2.id = 1; return 0; }
这样看,是不是很明了了呢?再定义一个结构体也很方便,直接STRUCT(structName)即可。
总结:
-#运算符用于在预处理期将宏参数转换为字符串
-##运算符用于在预处理期粘连两个标识符
-编译器不知道#和##运算符的存在
-#和##运算符只在宏定义中有效