C语言天花板——指针(进阶2)

好久不见了各位,甚是想念啊!!!🎶🎶🎶

文章接上次的指针(进阶1)(http://t.csdnimg.cn/c39SJ),我们继续发车咯🚗🚗🚗

 

五、函数指针

 上次我们只是浅说了一下函数指针,我们知道了函数指针是函数的地址,&函数名直接用函数名都能代表函数的地址

  函数返回类型 (* 指针名称)(参数类型) = &函数名 

指针类型:函数返回类型(*)(参数类型) = &函数名

当我们进行函数的调用的时候:我们不仅仅可以用函数名来调用函数了,我们可以解引用刚刚的函数指针来调用函数,当然我们不进行解引用也是可以正常调用的,因为函数名本身就可以代表函数的地址.

六、函数指针数组

指针数组:

char* arr[5] // 字符指针数组----数组-----存放的是字符指针

int* arr2[5] // 整形指针数组----数组------存放的是整形指针

那函数指针顾名思义就是数组-----存放的是函数指针

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int main()
{int (*pf1)(int, int) = &Add;int (*pf2)(int, int) = ⋐//数组中存放类型相同的多个元素int (* pfArr[4])(int, int) = {&Add, &Sub};//pfArr 是函数指针数组 - 存放函数指针的数组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 input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret = %d\n", ret);break;case 2:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出计算器\n");break;default:printf("选择错误, 重新选择\n");break;}} while (input);return 0;
}

我们可以看到运行结果也可以正常的实现计算机功能,但是不好,不好到哪了呢?代码有冗余的地方,就是下面红色圈起来的地方 :

而且还有一个不好的地方就是如果我想要再添加别的函数,那么我还是要写一边一摸一样的四行代码,如果是稍微复杂的函数功能,代码可读性就会下降

那我们用函数指针数组的方式再来改进一下,我们把加减乘除的四个函数的函数地址放到一个函数指针数组里面,代码实现如下:

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;do{menu();printf("请选择:>");scanf("%d", &input);//函数指针数组 - 转移表int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div};//                          0     1     2   3    4if (0 == input){printf("退出计算器\n");}else if (input >= 1 && input <= 4){printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else{printf("选择错误,重新选择!\n");}} while (input);return 0;
}

 可以看到效果依旧是计数器,但是消除了代码冗余,代码更加简洁,并且如果我们想要继续添加新功能的话,只需要将函数的地址增添到函数指针数组中就好了

七、指向函数指针数组的指针(拓展一下)

 刚刚我们提到了函数指针数组,它是用来存放函数指针的一种数组,那么是数组就会有地址,那么将这个地址取出来,那这个地址就是指向函数指针数组的指针

但这个了解就好,实例中几乎不会用 🥯🥯🥯

八、回调函数

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

概念很不好懂,我们直接上例子。

还是刚刚的计数器的例子,我们刚刚说代码有冗余

然后我们想能不能定义一个函数,然后每次调用函数,只用调用那一个函数就可以实现加减乘除该多好啊,于是我们有了以下代码:

这里的calc函数就是一个回调函数,他一个函数可以实现多个函数的功能,只需要把想要的函数地址传过去,然后calc就会帮你调用它来实现想要的功能,源代码和编译结果如下:

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;int ret = 0;printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误, 重新选择\n");break;}} while (input);return 0;
}

回调函数最典型的例子就是qsort函数,大家可以跳转到我的qsort函数的文章中来进一步学习(http://t.csdnimg.cn/3vGtz) 。🎈🎈🎈

那么今天对于指针的更深一步学习就到这里了,感谢大家的观看。🌹🌹🌹

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

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

相关文章

网卡唯一标识你了解吗?MAC地址详解

本文内容&#xff1a; MAC地址概述 MAC地址组成 单播、组播、广播MAC地址 本地管理和全球管理MAC地址 一、MAC地址概述 MAC地址&#xff08;Media Access Control Address&#xff09;的全称叫做媒体访问控制地址&#xff0c;也称作局域网地址&#xff0c;以太网地址或者物…

爆了,AI表情包制作全攻略揭秘

今天来一篇绝对的干货 在AI如火如荼的今天&#xff0c;各行各业各个领域都有AI的身影&#xff0c;今天给大家一篇AI制作表情包的教程&#xff0c;希望大家都能在AI的浪潮中找到自己的位置 一、制作篇 正常来说需要使用 midjourney&#xff0c;但是目前限制比较多&#xff0c…

【Vue3】3-2 : 组件的概念及组件的基本使用方式

本书目录&#xff1a;点击进入 一、组件的概念 1.1、为什么要做成组件 1.2、【案例】评分组件与按钮组件的抽离过程 二、组件的使用 - 抽离结构 2.1、【案例】简易首页 &#xff1e; 效果 &#xff1e; 代码 - 原始 &#xff1e; 代码 - 组件抽离结构 &#xff1e;…

如何在Windows 10/11的防火墙中禁止和允许某个应用程序,这里提供详细步骤

想阻止应用程序访问互联网吗&#xff1f;以下是如何通过简单的步骤阻止和允许Windows防火墙中的程序。​ 一般来说&#xff0c;大多数用户永远不需要担心应用程序访问互联网。然而&#xff0c;在某些情况下&#xff0c;你需要限制应用程序访问互联网。 例如&#xff0c;有问题…

jmeter--4.参数化的方式

目录 1. 用户定义的变量 2. 用户参数 3. 函数助手 3.1 time获取当前时间 3.2 Random随机数 3.3 随机字符串函数 3.4 字符串变更为大写 4. CSV数据文件设置 5. 接口关联--正则和json等提取 1. 用户定义的变量 线程组->添加->配置元件->用户定义的变量 引用方…

安达发|APS工序排程甘特图功能介绍

工序排程甘特图的主要功能 1. 显示工序时间安排&#xff1a;工序排程甘特图可以清晰地展示生产过程中各个工序的开始时间、结束时间和持续时间&#xff0c;从而帮助企业了解生产过程中各个环节的时间安排。 2. 显示工序进度情况&#xff1a;通过工序排程甘特图&#xff0c;企业…

计算机毕业设计-----SSH高校科研管理系统平台

项目介绍 本项目包含超级管理员、管理员、教师三种角色&#xff1b; 超级管理员角色包含以下功能&#xff1a; 登录,教师管理,管理员管理等功能。 管理员角色包含以下功能&#xff1a; 登录,专业参赛奖项管理,科技论文发表管理,出版专业著作管理,科研项目立项管理,科研项目结…

数据结构--排序

参考【算法】排序算法之希尔排序 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/122632213 1. 排序的定义 2. 插入排序 2.1 直接插入排序 在插入第i&#xff08;i>1)个记录时&#xff0c;前面的i-1个记录已经排好序 void insertSort(int r[],int n) {for(int i2;i<…

《WebKit 技术内幕》之二: HTML 网页和结构

第二章 HTML 网页和结构 HTML网页是利用HTML语言编写的文档&#xff0c;HTML是半结构化的数据表现方式&#xff0c;它的结构特征可以归纳为&#xff1a;树状结构、层次结构和框结构。 1.网页构成 1.1 基本元素和树状结构 HTML网页使用HTML语言撰写的文档&#xff0c;发展到今…

合适的索引顺序

一.前言 正确的顺序依赖于使用索引的查询,并且同时需要考虑如何更好地满足排序和分组的需要。因为哈希或者其他类型的索引并不会像 B-Tree索引一样顺序存储数据,所以这里只针对B-Tree展开讨论。 二.合适的索引顺序 1. 概念 对于如何选择索引顺序有一个经验法则: 将选择性最…

hcip-4

ISIS:中央系统到中央系统 基于OSI模型开发&#xff1b; 集成的ISIS&#xff0c;基于OSI开发后转移到TCP/IP模型执行&#xff1b; 故集成的ISIS既可以在OSI模型&#xff0c;也可在TCP/IP模型工作&#xff1b; ISIS是在ISP中使用的一个IGP协议&#xff0c;其归属于无类别链路状…

HashData湖仓一体方案:方案概览与Hive数据同步

随着云计算、大数据、AI的发展和普及&#xff0c;各行各业的业务场景日益复杂&#xff0c;数据呈现出大规模、多样性的特点&#xff0c;企业对数据仓库的需求也进一步拓展至对多元化数据实时处理的场景。 数据湖是多元数据存储与使用的便捷选择&#xff0c;而云原生具有数据资…

谷歌aab包在Android 14闪退而apk没问题(targetsdk 34)

问题原因 Unity应用(target SDK 34)上线到GooglePlay&#xff0c;有用户反馈fold5设备上&#xff08;Android14系统&#xff09;疯狂闪退&#xff0c;经测试&#xff0c;在小米手机Android14系统的版本复现成功了&#xff0c;奇怪的是apk直接安装没问题&#xff0c;而打包成aa…

浙大恩特客户资源管理系统CrmBasicAction.entcrm接口存在任意文件上传漏洞

@[toc] 免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. 浙大恩特客户资源管理系统接口简介 微信公…

搭建eureka-server

首先要搭建注册中心服务端&#xff1a;eureka-server&#xff0c;这必须是一个独立的微服务 1.创建项目 创建maven工程即可 2.引入依赖 提示&#xff1a;starter是springboot中的自动装配&#xff0c;在这个依赖里&#xff0c;已经帮我们把eureka的所有配置都做好了 <de…

构建一个最新版本 Maven 项目

文章目录 构建一个最新版本 Maven 项目1. 所用各种软件的版本2. 踩过的坑3. 构建项目过程4. 项目打包方式 构建一个最新版本 Maven 项目 截止 2024 年 1 月 13 日&#xff0c;Apache 官网上 Maven 的最新安全版本为 3.9.6&#xff0c;下载、安装及配置方法见之前的博客&#x…

SUKER书客重磅发布—书客Sun立式护眼台灯,掀起光学护眼养眼新纪元

SUKER书客作为台灯行业的引路者&#xff0c;在台灯中取得颠覆行业的成绩后&#xff0c;并没有就此停下脚步&#xff0c;终于在近日的新品发布中揭露了神秘面纱——书客Sun立式护眼灯&#xff0c;再现创新光学护眼养眼&#xff0c;突破行业护眼天花板&#xff0c;帮助孩子们预防…

一步直达 - CDPATH的妙用

文章目录 一步直达 - CDPATH的妙用更多信息 一步直达 - CDPATH的妙用 适用于贼长的目录路径 如果你的目录路径贼长&#xff0c;一定要用一下这个命令。 变量CDPATH定义了目录的搜索路径&#xff0c;这个设置特别适合经常需要进入到某个目录&#xff0c;此时这个设置就及其有用…

直线模组的适用性

直线模组是由直线运动导向部件和滑块组成的&#xff0c;是一种常见的机械元件&#xff0c;主要用于传输力和运动&#xff0c;其结构简洁&#xff0c;安装方便&#xff0c;耐用高效&#xff0c;广泛应用于各行各业&#xff0c;并且可以通过调整其长度和直径来适应不同的应用场景…

rust跟我学:文件时间属性获得方法

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎样获得杀毒软件的病毒库时间的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址…