• 进阶指针(二)


    图片来源于网络
    #国庆发生的那些事儿#

    ✨博客主页:小钱编程成长记
    🎈博客专栏:进阶C语言
    🎈推荐相关博文:进阶指针(一)

    6.函数指针数组

    数组是一个存放相同类型数据的存储空间,我们先来回顾一下已经学过的指针数组。

    以整型指针数组为例:
    整型数据的地址存到一个数组中,数组中每个元素都是int* 类型的,这种数组就叫作整型指针数组。

    //以整型指针数组为例
    int a = 0;
    int b = 0;
    int c = 0;
    int* parr[3] = { &a, &b, &c };//整型指针数组,把整型数据的地址存到数组中,数组名为parr,数组中有3个元素,每个元素都是int* 类型的。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    函数指针数组和整型指针数组是类似的。

    函数指针数组:
    把函数的地址存到一个数组中,数组中每个元素都是函数指针类型的,这种数组就是函数指针数组。

    int Add(int x, int y)
    {
    	return x + y;
    }
    int Sub(int x, int y)
    {
    	return x - y;
    }
    int(*pfarr[2])(int, int) = { Add, Sub };//函数名也是函数的地址
    //函数指针数组---数组名为pfarr,数组中有2个元素,数组中元素是函数指针类型的类型为int(*)(int, int),函数指针指向的函数参数是int类型的,返回类型也是int类型的。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    函数名也是函数的地址;
    函数指针数组------数组名为pfarr,数组中有2个元素,数组中元素是函数指针类型的类型为int(*)(int, int) ,函数指针指向的函数参数是int类型的,返回类型也是int类型的,类型中的参数可写可不写,但参数类型一定要写。

    注: 函数指针数组的用途:转移表

    6.1例子

    写一个有+ - * / 四种功能的计算器程序。

    //计算器
    #include  
    void mune()
    {
    	printf("********************\n");
    	printf("*** 1.add  2.sub ***\n");
    	printf("*** 3.mul  4.div ***\n");
    	printf("***    0.exit    ***\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 a = 0;
    	int b = 0;
    	int ret = 0;
    	do
    	{
    		mune();
    		printf("请选择:");
    		scanf("%d", &input);
    		switch(input)
    		{
    		case 1:
    			printf("请输入操作数:");
    			scanf("%d %d", &a, &b);
    			ret = add(a, b);
    			printf("%d\n", ret);
    			break;
    		case 2:
    			printf("请输入操作数:");
    			scanf("%d %d", &a, &b);
    			ret = (a, b);
    			printf("%d\n", ret);
    			break;
    		case 3:
    			printf("请输入操作数:");
    			scanf("%d %d", &a, &b);
    			ret = mul(a, b);
    			printf("%d\n", ret);
    			break;
    		case 4:
    			printf("请输入操作数:");
    			scanf("%d %d", &a, &b);
    			ret = div(a, b);
    			printf("%d\n", ret);
    			break;
    		case 0:
    			printf("退出程序\n");
    			break;
    		default:
    			printf("选择错误\n");
    			break;
    		}
    	} while (input);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    我们可以观察到用switch写有点冗余,若计算函数增多,则switch语句会越来越长。
    我们发现这些计算函数的参数个数、类型,和返回类型都一样。可以写成写成函数指针数组
    将计算函数写成函数指针数组的形式在main函数中使用,会比使用switch语句更加简洁。

    使用函数指针数组实现计算器:

    思路:

    1. 写个菜单, 1 ~ 4是计算,0是退出程序,其它的数字是输入错误。
    2. 写四个计算函数。
    3. 使用do…while循环。
    4. 将四个函数存到函数指针数组,用函数指针数组中的元素进行调用函数计算。
    #include 
    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;
    	do
    	{
    		menu();
    		int(*pfunarr[])(int, int) = {NULL, Add, Sub, Mul, Div};//函数指针数组在这种场景下称为转移表。
    		//数组中多存一个空指针NULL,是为了将函数地址的下标挤到1 ~ 4方便使用这些函数地址。
    		
    		printf("请选择:");
    		scanf("%d", &input);
    		if (input >= 1 && input <= 4)
    		{
    			printf("请输入:");
    			scanf("%d %d", &x, &y);
    			int ret = pfunarr[input](x, y);
    			printf("%d\n", ret);
    		}
    		else if(input == 0)
    		{
    			printf("退出程序\n");
    		}
    		else
    		{
    			printf("选择错误\n");
    		}
    		printf("\n\n");
    	} while (input);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    使用函数指针数组有一个约束条件: 这些函数的参数类型、个数,返回类型必须都一样。

    7.指向函数指针数组的指针

    指向函数指针数组的指针是一个指针
    指针指向一个数组,数组的元素都是函数指针

    我们先想一想指向整型指针数组的指针:

    int* arr[] = { &a, &b, &c };
    int* (*p)[3] = &arr;//这里[]里的数据一定要写
    //p先与*结合,说明p是指针,
    //再与[]结合,说明p指向的是数组,数组中有三个元素,元素的类型是int*; 
    //p中存放的是数组的地址。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指向函数指针数组的指针也是类似的:

    int (*pfarr[])(int, int) = { NULL, Add, Sub, Mul, Div };
    p = &pfarr;
    //如果p是指针,那么p就是能够指向函数指针数组pfarr的指针
    //指针p的类型是什么呢?
    //p是指针,p指向的是数组,数组中有5个元素,元素是函数指针类型的,类型为int (*)(int, int)
    //所以p的类型为int (*(*)[5])(int, int)
    int (*(*p)[5])(int, int) = &pfarr;//p首先和*结合说明p是指针
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    类型中的[]里的元素个数一定要写,而且要写对;这个数据一变,整个类型也会发生改变。

    8. 回调函数(上)

    回调函数就是一个通过函数指针调用的函数。
    如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
    回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    8.1举例:

    将计算两个数的过程专门用一个函数封装起来,将四个计算函数的指针作为参数传递给封装函数,在封装函数内用函数指针调用计算函数进行运算,此时计算函数就被称为回调函数。

    #include 
    
    //菜单
    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;
    }
    
    void calc(int(*pf)(int, int))
    {
    	int x = 0;
    	int y = 0;
    	printf("请输入:");
    	scanf("%d %d", &x, &y);
    	int ret = pf(x, y);//此时pf指向的函数叫作回调函数
    	printf("%d\n", ret);
    }
    
    int main()
    {
    	int input = 0;
    	do
    	{
    		menu();
    		printf("请选择:");
    		scanf("%d", &input);
    		if (input >= 1 && input <= 4)
    		{
    			int(*pfarr[])(int, int) = { NULL, Add, Sub, Mul, Div };
    			calc(pfarr[input]);
    		}
    		else if (input == 0)
    		{
    			printf("退出程序\n");
    		}
    		else
    		{
    			printf("输入错误\n");
    		}
    		printf("\n\n");
    	} while (input);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    图解:
    在这里插入图片描述

    总结

    本篇文章我们学习了函数指针数组,指向函数指针数组的指针,一小部分回调函数,还有计算器的实现和优化。感谢大家的阅读!大家一起进步!

    点赞收藏加关注,C语言学习不迷路!
    图片来源于网络

  • 相关阅读:
    Android 13.0 USB鼠标右键改成返回键的功能实现
    C学生管理系统 头添加学生节点
    动手学习深度学习-《线性代数实现》
    【9】Docker的迁移与备份
    关于地图GIS开发事项的一次实践整理(上)
    MySQL最新2023年面试题及答案,汇总版(1)【MySQL最新2023年面试题及答案,汇总版-第三十一刊】
    保护网站安全:深入理解文件上传漏洞
    Springcloud Alibaba详细入门教程(一)
    MySQL主从搭建
    设计模式14、命令模式 Command
  • 原文地址:https://blog.csdn.net/xue_bian_cheng_/article/details/133410721