进阶指针(二)

news2024/11/24 19:49:16

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

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

进阶指针(二)

  • 6.函数指针数组
    • 6.1例子
  • 7.指向函数指针数组的指针
  • 8. 回调函数(上)
    • 8.1举例:
  • 总结

6.函数指针数组

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

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

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

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

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

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类型的。

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

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

6.1例子

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

//计算器
#include <stdio.h> 
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;
}

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

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

思路:

  1. 写个菜单, 1 ~ 4是计算,0是退出程序,其它的数字是输入错误。
  2. 写四个计算函数。
  3. 使用do…while循环。
  4. 将四个函数存到函数指针数组,用函数指针数组中的元素进行调用函数计算。
#include <stdio.h>
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;
}
使用函数指针数组有一个约束条件: 这些函数的参数类型、个数,返回类型必须都一样。

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

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

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

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

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

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是指针
类型中的[]里的元素个数一定要写,而且要写对;这个数据一变,整个类型也会发生改变。

8. 回调函数(上)

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

8.1举例:

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

#include <stdio.h>

//菜单
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;
}

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

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1049898.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenHarmony自定义组件介绍

一、创建自定义组件 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为系统组件&#xff0c;由开发者定义的称为自定义组件。在进行 UI 界面开发时&#xff0c;通常不是简单的将系统组件进行组合使用&#xff0c;而是需要考虑代码可复用性、业务逻辑…

四、YApi的安装和配置

YApi是去哪儿网的前端技术中心的一个开源可视化接口管理平台。 创建接口项目 创建接口 编写接口

中文读唇总动员:CNVSRC 2023 视觉语音识别挑战赛启动

由 NCMMSC 2023 组委会发起&#xff0c;清华大学、北京邮电大学、海天瑞声、语音之家共同主办的 CNVSRC 2023 中文连续视觉语音识别挑战赛即日启动&#xff0c;诚邀参与报名。 赛事官网&#xff1a;http://cnceleb.org/competition 视觉语音识别&#xff0c;也称唇语识别&…

为什么我的Windows 10笔记本电脑明明什么软件都没开,风扇却一直在转?

2023年9月29日&#xff0c;周五上午 这两天我的笔记本电脑一开机&#xff0c;风扇就一直在转&#xff0c;而且还没停过&#xff0c;挺吵的 即使什么软件都没开&#xff0c;还在那转&#xff0c;莫名其妙的。 后来我去任务管理器按照CPU使用情况来排序&#xff0c;发现原来是W…

如何自动转发接收的请求报头?

了解OpenTelemetry的朋友应该知道&#xff0c;为了将率属于同一个请求的多个操作&#xff08;Span&#xff09;串起来&#xff0c;上游应用会生成一个唯一的TraceId。在进行跨应用的Web调用时&#xff0c;这个TraceId和代表跟踪操作标识的SpanID一并发给目标应用&#xff0c;W3…

经过认证的工具链对安全关键型应用意味着什么?

作者&#xff1a;IAR 安全关键型应用&#xff0c;在很多人看来是个专业的词汇&#xff0c;但其实它离我们的日常生活很近&#xff0c;比如汽车驾驶系统、飞机控制系统、电梯运行系统、医疗设备等与我们息息相关的事物都可以纳入安全关键型应用的范畴。 对于这类应用&#xff…

SAP入门到放弃系列之QM检验计划-Part1

文章目录 一、概述1.1、检验计划抬头1.2、检验计划工序 二、系统操作2.1、测试数据准备&#xff1a;2.2、创建检验计划 一、概述 检验计划是用来描述如何对一种或多种物料进行质量检验操作的主数据。在检验计划中&#xff0c;可以定义检验的工序顺序以及可用于检验特征的数据规…

排序:归并(Merge)排序算法分析

1.归并操作 归并:把两个或多个已经有序的序列合并成一个。 2路归并&#xff1a;二合一k路归并&#xff1a;k合一结论:m路归并&#xff0c;每选出一个元素需要对比关键字m-1次。 2.算法思想 核心操作:把数组内的两个有序序列归并为一个。 例如&#xff1a; 3.代码实现 将…

数据集笔记: Porto

数据来源&#xff1a;Taxi Trajectory Data_数据集-阿里云天池 (aliyun.com) 1 数据介绍 葡萄牙波尔图市运行的所有442辆出租车的全年轨迹&#xff08;从2013年7月1日至2014年6月30日&#xff09; 2 读取数据 import pandas as pdtrapd.read_csv(C:/Users/16000/Download…

C语言中动态内存管理

前言&#xff1a;为什么存在动态内存分配&#xff0c;为什么要用动态内存分配&#xff0c;动态内存分配的意义。鸡汤&#xff1a;有了坚持不一定成功,但没有坚持&#xff0c;就注定失败&#xff0c;各位也要努力坚持提升自己&#xff01; 动态内存分配 动态内存函数&#xff1a…

【Linux】 du 命令使用

问题 No space left on device 请求接口返回 java.io.IOException: No space left on device 设备上没有剩余空间 怎么解决问题&#xff1a; 查看这篇文章&#xff1a;一次 linux 服务器磁盘使用情况排查 我们提到命令&#xff1a;du -sh * 到底这个命令是干什么的咱们…

EXPLAIN 语句输出的各个列解释

title: “EXPLAIN 语句输出的各个列解释” createTime: 2022-03-06T15:52:4108:00 updateTime: 2022-03-06T15:52:4108:00 draft: false author: “ggball” tags: [“mysql”] categories: [“db”] description: “” EXPLAIN 语句输出的各个列解释 列名描述id在一个大的查…

多线程(基础)

文章目录 1. 线程的声明周期1.1 JDK 中用 Thread.State 枚举表示了线程的几种状态1.2 线程状态转换图 2. 线程的同步2.1 Synchronized 线程同步机制 3. 互斥锁3.1 注意事项和细节3.2 守护线程 setDaemon()方法 4. 线程的死锁5. 释放锁6. 课后练习 1. 线程的声明周期 1.1 JDK 中…

四通道信息融合下的齿轮箱故障诊断(Python代码,SVM模型和CNN模型进行对比实验,解压缩即可运行,有详细中文注释)

1.效果运行视频&#xff1a;四通道信息融合下的齿轮箱故障诊断&#xff08;Python代码&#xff0c;SVM模型和CNN模型进行对比实验&#xff09;_哔哩哔哩_bilibili 用到的库&#xff1a; 2.数据集介绍&#xff1a;数据免费下载链接&#xff08;不要积分&#xff09;&#xff1a…

什么是跨站请求伪造(CSRF)攻击?如何防止它?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是跨站请求伪造&#xff08;CSRF&#xff09;攻击&#xff1f;⭐ 如何防止CSRF攻击&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦…

xxl-job 2.2之后版本高版本executor未授权访问漏洞

xxl-job 低版本executor未授权访问 低版本的executor未授权访问漏洞是 POST /run HTTP/1.1 Host: your-ip:9999 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like G…

26663-2011 大型液压安全联轴器 课堂随笔

声明 本文是学习GB-T 26663-2011 大型液压安全联轴器. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了大型液压安全联轴器的分类、技术要求、试验方法及检验规则等。 本标准适用于联接两同轴线的传动轴系&#xff0c;可起到限制…

基于Linux 系统聊天室登录与注册实现(03)

上一篇我们已经讲了如何搭建一个多线程的服务器模型&#xff0c;可以支持多个客户端同时连接服务器&#xff0c;本篇我们来实现多个客户端&#xff0c;如何实现向服务器注册信息&#xff0c;并实现登录的功能。 数据结构 接着上一篇的实例代码继续增加功能。要实现注册和登录…

(Java)关于easyExcel合并单元格

今天过客遇到一个需要合并单元格的业务&#xff0c;但是之前过客用的一直是easyExcel框架&#xff0c;所以这次也不想去使用其他的框架&#xff0c;今天就跟大家讲讲easyExcel怎么进行单元格的合并。 首先使用easyExcel进行导出的实体类一样是依据之前那样写&#xff0c;之后在…

宠物玩具在欧洲销售CE认证EN71测试标准

CE认证的EN71测试宠物玩具办理&#xff1a; 宠物玩具是用来给宠物玩耍&#xff0c;基于将宠物作为人类伙伴关系而诞生的一种玩具类型&#xff0c;这种玩具的存在就是让人类和自己的宠物真正的互动起来&#xff0c;在情感上面得到更大的交流和互动。 那么宠物玩具出口到欧盟市场…