C语言--指针进阶2

前言

本篇文章我们将继续学习指针进阶的有关内容

函数指针

我们依然用类比的方法1来理解函数指针这一全新的概念,如图1
图1

我们用一段代码来验证一下:

int Add(int x, int y)
{
    
    
	return x+y;
}

int main()
{
    
    
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

打印结果如图2
图2
进一步验证了函数指针确实是存放函数的地址。
值得注意的是,函数名和取地址函数名的结果是一样的,这有别于数组名和取地址数组名

那么如果我们想用一个指针变量来存放函数的地址该怎么书写呢?
同样是类比数组指针的写法,如下:

int (*pf)(int,int) = Add;

这里的pf就是函数指针,在书写的时候只用交代类型即可(int char float等),不需要把形参也写进去

如果我们想通过函数指针调用这个函数怎么书写呢?
如下代码:

int Add(int x, int y)
{
    
    
	return x+y;
}

int main()
{
    
    
	int (*pf)(int, int) = Add;
    printf("%d\n", (*pf)(3, 5));
	printf("%d\n", pf(3, 5));
	printf("%d\n", Add(3, 5));
	return 0;
}

打印结果如图3

图3
所以以上三种形式的书写均可实现函数的调用。

来看两段有趣的代码
先来看第一个:

(*(void (*)())0)();

对于这样复杂的代码,我们来逐步地分析:
1,将0强制类型转换为void (*)() 类型的函数指针。
2,这就意味着0地址处放着一个函数,函数没有参数,返回类型是void。
3,调用0地址处对这个函数。

我们再来看第二个:

void (*signal(int , void(*)(int)))(int);

我们同样来逐步分析:(注意这里的signal并没有和结合)
1,上述的代码是一个函数的声明。
2,函数的名字是signal。
3,函数的参数第一个是int,第二个是void(
)(int)类型的函数指针。
4,该函数指针指向的函数参数是int,返回类型是void。

5,signal函数的返回类型也是一个函数指针。
6,该函数指针指向的函数参数是int,返回类型是void。
这样讲可能还是不好理解,我们再对代码进行一下简化:

typedef int* ptr_t;
typedef void(*pf_t)(int);//将void(*)(int)类型重新起个别名pf_t
int main()
{
    
    
      void(* signal(int,void(*)(int)))(int);
      pf_t signal(int,pf_t);
      return 0;
}

函数指针数组

同样是类比数组指针,比如整型数组指针就是存放整形指针的数组,那么函数指针数组就是存放函数指针的数组
我们来看下面这段代码:

int Add(int x, int y)
{
    
    
	return x+y;
}

int Sub(int x, int y)
{
    
    
	return x - y;
}

int Mul(int x, int y)
{
    
    
	return x * y;
}
int Div(int x, int y)
{
    
    
	return x / y;
}
int main()
{
    
    
	int (*pf[4])(int, int) = {
    
     Add,Sub,Mul,Div };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
    
    
		int ret = pf[i](8, 4);
		printf("%d\n", ret);
	}
	return 0;
}

打印结果如图4
图4
那么函数指针数组有什么作用呢?
我们可以通过函数指针数组来实现一个简单的计算器:

void menu()
{
    
    
	printf("*********************************************\n");
	printf("**********    1,add     2,sub   *************\n");
	printf("**********    3,mul     4,div   *************\n");
	printf("**********    0,exit            *************\n");
	printf("*********************************************\n");
}
int Add(int x, int y)
{
    
    
	return x+y;
}

int Sub(int x, int y)
{
    
    
	return x - y;
}

int Mul(int x, int y)
{
    
    
	return x * y;
}
int Div(int x, int y)
{
    
    
	return x / y;
}
int main()
{
    
    
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*pfArr[])(int, int) = {
    
     NULL,Add,Sub,Mul,Div };
	do
	{
    
    
		menu();
		printf("请选择: >");
		scanf_s("%d", &input);
		if (input == 0)
		{
    
    
			printf("退出计算器\n");
			break;
		}
		if (input >= 1 && input <= 4)
		{
    
    
			printf("请输入两个操作数:>");
			scanf_s("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("结果为%d\n", ret);
		}
		
	} while (input);
	return 0;
}

运行效果如图5
图5
这样我们就通过灵活使用函数指针数组,巧妙的简化了代码,防止冗长。

指向函数指针数组的指针

指向函数指针数组的指针的书写方式如下

int Add(int x, int y)
{
    
    
	return x + y;
}

int Sub(int x, int y)
{
    
    
	return x - y;
}
int main()
{
    
    
	int (*pf)(int, int) = Add;
	int (*pfArr[4])(int, int) = {
    
     Add,Sub };
	int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针变量
	return 0;
}

我们分步来理解这个式子
1,ppfArr是一个指针变量。
2,该指针变量指向的是一个数组,有四个元素。
3,该数组的每个元素类型是int (
)(int,int),是一个函数指针。

回调函数

我们先来看概念:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
那么回调函数具体怎么使用呢?看下面这段代码

int main()
{
    
    
	int input = 0;
	int x, y;
	int ret = 0;
	scanf_s("%d", &input);
	switch (input)
	{
    
    
	case 1:
		printf("请输入两个操作数:");
		scanf_s("%d %d", &x, &y);
		ret = Add(x, y);
		printf("%d\n", ret);
	case 2://Sub
	case 3://Mul
	case 4://Div
	}
	return 0;
}

我们会发现,case等于不同的数时,总会执行重复的语句。我们能不能这样思考:假设我们把这些重复的语句封装成一个函数,然后把不同运算的函数地址转过去调用呢?

我们定义一个Calc函数:

void Calc(int(*pf)(int, int))
{
    
    
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:");
	scanf_s("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
	
}

这样我们就实现了在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应的这样一个效果(即case等于不同的值是执行不同的响应)。

以上就是本章全部内容,下一章我们将运用回调函数的特性来模拟实现库函数–qsort(快速排序)。

猜你喜欢

转载自blog.csdn.net/m0_75233943/article/details/129211280