一、宏定义中 “#”知识点
#的作用是将“#”后面的宏参数进行字符串转换操作,也就是将#后面的参数 两边加上一对双引号,使其成为字符串。
这里面有几个知识点:
1、直接转换字符串,不展开。
这句话的意思是,带参数宏定义也是宏,而不是普通函数,所以要保留宏的基本特性,也就是全部替换,而不展开。示例代码:
#include <stdio.h>
#include <stdlib.h>
#define CONVERT(a) #a
int main(int argc, char *argv[]) {
int a = 6;
printf("res:%s\n", CONVERT(a));
return 0;
}
运行结果:
res:a
2、转换出的结果一定是“字符串”。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#define CONVERT(a) #a
int main(int argc, char *argv[]) {
printf("string print:%s\n", CONVERT(6)); // 字符串格式打印
printf("int print:%d\n", CONVERT(6)); // 整型打印
return 0;
}
运行结果如下:
string print:6
int print:4210688
二、宏定义中 ## 知识点
1、应用场景
## 的 应用场景主要是 函数指针的动态绑定,使用该宏参数,可以实现 同类型 函数指针 赋值或者引用的 方便操作。这个后面会有代码详细分析。
2、“##”的作用是将 左右两边的参数做整体字符串拼接替换
不管 ## 左右两边是字母还是数字,最终都是作为 一个整体 拼接成一个字符串。代码如下:
#include <stdio.h>
#include <stdlib.h>
#define FUNC(a) func_##a
void func_1(void)
{
printf("this is func 1.\n");
}
void func_2(void)
{
printf("this is func 2.\n");
}
int main(int argc, char *argv[]) {
FUNC(1)();
FUNC(2)();
return 0;
}
3、经过 ## 替换后的内容必须 有一个 同名的 变量与之对应。
比如 运行 FUNC(1),必须要保证 有一个 func_1 函数, 同样的 FUNC(2) 对应 func_2函数,如果没有,编译会报错,提示对象未 定义。
4、 只拼接,不展开。
这个类似于 # ,也是原封不动的转换,并不会进一步展开,比如:
int i = 1;
FUNC(i)();
尽管 i = 1,但是 FUNC(i) 的结果是 func_i,而不是 func_1.,所以我们不能够使用 ## 的时候,自作聪明的使用变量。
5、## 操作 动态函数指针 案例代码
在 知识点3中,我们提到 不能使用 变量 和 ## 的结合来实现 动态绑定函数指针,那么我们的程序代码就相对不太方便,尤其是我们 想要定义 一系列的 函数,这些函数主题名相同,但是序号不同,那么这个时候,就推荐使用 结构体 数据结构,代码如下所示:
#include <stdio.h>
#include <stdlib.h>
#define FUNC(a) func_##a
void func_0(void)
{
printf("this is func 0.\n");
}
void func_1(void)
{
printf("this is func 1.\n");
}
void func_2(void)
{
printf("this is func 2.\n");
}
//包含函数 序号、 函数指针的 结构体
typedef struct func_t{
int id;
void (*func)(void);
} func_map_t;
// 函数结构体 数组
static func_map_t g_func_map[3] = {
{0, FUNC(0)},
{1, FUNC(1)},
{2, FUNC(2)},
};
int main(int argc, char *argv[]) {
g_func_map[0].func(); //间接调用 序号对应的 函数。
g_func_map[1].func();
g_func_map[2].func();
return 0;
}
这里需要注意的是,我们在定义 上面的 g_func_map 数组 时,需要 在 函数func_0、func_1、func_2之后,这可能是因为编译器先后顺序的原因,如果g_func_map 在这三个函数之前,那么会出现 编译报错信息,显示 这3个函数未定义,可以简单的认为,在定义的时候,数据类型从 前面 查找, 而不是全局查找。
运行结果如下:
this is func 0.
this is func 1.
this is func 2.