C语言 带参数宏定义中 # 和 ## 知识点总结、代码分析

   

目录

一、宏定义中 “#”知识点

1、直接转换字符串,不展开。

2、转换出的结果一定是“字符串”。

二、宏定义中 ## 知识点

1、应用场景

2、“##”的作用是将 左右两边的参数做整体字符串拼接替换

3、经过 ## 替换后的内容必须 有一个 同名的 变量与之对应。

4、 只拼接,不展开。

5、## 操作 动态函数指针 案例代码


一、宏定义中 “#”知识点

  #的作用是将“#”后面的宏参数进行字符串转换操作,也就是将#后面的参数 两边加上一对双引号,使其成为字符串。

这里面有几个知识点:

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.
发布了247 篇原创文章 · 获赞 257 · 访问量 62万+

猜你喜欢

转载自blog.csdn.net/u012351051/article/details/101038899