当前位置: 首页 > news >正文

【C语言进阶篇】指针都学完了吧!那回调函数的应用我不允许还有人不会!


在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 📋 前言
  • 💬 函数指针数组
    • 💭 函数指针数组的定义
    • 💭 函数指针数组的应用 —— 转移表
        • 💻 代码改进
  • 💬 指向函数指针数组的指针
  • 💬 回调函数
    • 💭 利用回调函数进行代码改进
  • 📝全篇总结

📋 前言

  🌈hello! 各位宝子们大家好啊,上节课我们学习了函数指针,而函数指针有一个非常大的用途就是实现回调函数!
  ⛳️在了解回调函数之前我们还需要学习一下函数指针数组的概念!
  📚本期文章收录在《C语言进阶篇》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

🔥 注:VS2022 等C语言学习工具都在《学习工具专栏》, 还有各种实用调试技巧有兴趣可以去看看!

💬 函数指针数组

  ⛳️既然我们有指针数组的概念,那么函数本身也是一个地址啊!函数指针数组 和 指针数组只不过多了函数俩字 ,那么函数指针数组该如何定义呢?

💭 函数指针数组的定义

  ⛳️假设我们要写一个计算器,加减乘除4个部分的函数但是函数参数都是一样的。想把他放到一个函数指针数组里面该怎么办呢?

  • 函数指针我们知道怎么定义但是,加了数组俩字该怎么定义呢?

📚 代码演示:

#include <stdio.h>int Sum(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 (*pf1)(int, int) = Sum;int (*pf2)(int, int) = Sub;int (*pf3)(int, int) = Mul;int (*pf4)(int, int) = Div;//函数指针数组int (*pfArr[4])(int, int) = {Sum,Sub,Mul,Div};return 0;
}

大家看函数指针数组就是这样定义的,由于【】号的结合性比 * 号高所以 pfArr就先和【】号结合,所以pfArr[ 4 ] 表示这个指针是个数组。

  • * 号则代表pfArr[ 4 ] 是个指针
  • int(*)(int , int ) 代表了他是个函数指针类型的
  • 所以 nt (*pfArr[4])(int, int) 是函数指针数组类型的

💭 函数指针数组的应用 —— 转移表

前面写的计算器,如果按照我们以前思路写的代码会非常冗余,那么我们学了函数指针数组有没有可能吧代码优化下嘛?

  • 这时就要用到函数指针数组的应用 —— 转移表了
  • 我们来看一下:

📚 未改变前:

#include <stdio.h>int Sum(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 menu()
{printf("***********************\n");printf("***** 1:add 2:sub ***** \n");printf("***** 3:mul 4:div *****\n");printf("***** 0.exit      *****\n");printf("***********************\n");
}
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("请输入俩个操作数:");scanf("%d %d", &x, &y);ret = Sum(x, y);printf("%d\n", ret);break;case 2:printf("请输入俩个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("请输入俩个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("请输入俩个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出计算器!\n");break;default:printf("选择错误,请重新输入!\n");break;}} while (input);return 0;
}

💻 代码改进

这里我们就带代码进行了改进,使得main() 函数里面代码量大大减少。

  • 这里由于我们选择时的数字要和数组下标一样
  • 所以我们填充一个空指针 NULL 让每个数组下标和我们的操作数对应!
#include <stdio.h>
int Sum(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 menu()
{printf("***********************\n");printf("***** 1:add 2:sub ***** \n");printf("***** 3:mul 4:div *****\n");printf("***** 0.exit      *****\n");printf("***********************\n");
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请输入->");scanf("%d", &input);//函数指针数组 —— 转移表int(*pfArr[5])(int, int) = {NULL ,Sum,Sub,Mul,Div};if (input >= 1 && input <= 4){printf("请输入操作数:");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);printf("%d\n", ret);}else if (input == 0){return 0;}else{printf("选择错误请重新输入;\n");}} while (input);return 0;
}

💬 指向函数指针数组的指针

这里我们又进行了一遍套娃,指针数组既然我们能接收那么。有没有指向函数指针数组的指针呢?答案是有的。

  • 数组指针类型 int (*p)[ 5 ]
  • 函数指针类型 int (*pf)(int, int)
  • 函数指针数组类型 int (*pf [ 5 ])(int, int)
  • 指向函数指针数组的指针 int (*(*pf)[ 5 ])(int, int)

这里我们可以怎么样理解呢?首先在指向函数指针数组的指针不要硬写在我们函数指针数组的类型上进行改变!

  • 函数指针数组类型 int (*pf [ 5 ])(int, int)
  • 我们首选需要一个指向函数指针数组的指针
  • 那么pf 就不能和左边的 * 结合 因为 int (* [ 5 ])(int, int) 代表了函数指针数组类型而我们想要接收他就需要一个指针 *pf
  • 那么把它括起来就是指向函数指针数组的指针了
  • int (*(*pf)[ 5 ])(int, int)

📚 代码演示:

在这里插入图片描述

💬 回调函数

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

  • 怎么应用呢?函数指针的应用其实就是回调函数
  • 我们前面改进计算器的的时候用函数指针数组进行改进
  • 那么我们下面就对计算器用回调函数的方式进行改进

回调函数我的理解就是通过函数指针的方式接收函数的地址,从而调用它。让一个函数可以调用多个同类型的函数的功能在特定的情况给我传输函数地址从而使用它。

  • 上面计算器的实现用switch 语句实现的时候每一个case语句都是相同的除了函数的实现方法不同。这样就导致代码过于冗余!
    在这里插入图片描述
    而这我们就可以用一个回调函数进行改进,让这些代码都在一个函数里面。每次使用那个函数直接传送给我们的地址就行了!

📚 代码演示:

这样铁汁们看是不是就避免的代码冗余的问题,我们只需要把函数地址传过来就可以了,输入什么选择就传什么函数从而去调用他!

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

在这里插入图片描述

💭 利用回调函数进行代码改进

📚 代码演示:

#include <stdio.h>
int Sum(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 menu()
{printf("***********************\n");printf("***** 1:add 2:sub ***** \n");printf("***** 3:mul 4:div *****\n");printf("***** 0.exit      *****\n");printf("***********************\n");
}
void calc(int (*pf)(int x, int y))
{int x = 0;int y = 0;printf("请输入操作数;");scanf("%d %d", &x, &y);int ret = pf(x, y);printf("%d\n", ret);
}
int main()
{int input = 0;do{menu();printf("请输入->");scanf("%d", &input);switch (input){case 1:calc(Sum);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;
}

📝全篇总结

✅ 归纳:
好了以上就是回调函数的使用和讲解以及函数指针数组的概念大家好好理解一下吧!
  函数指针数组
  函数指针数组的使用
  回调函数的概念
  回调函数的应用
☁️ 好了今天的知识全是干货不知道各位铁汁们学到了没有呢!快拿去用吧!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

相关文章:

【C语言进阶篇】指针都学完了吧!那回调函数的应用我不允许还有人不会!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言&#x1f4ac; 函数指针数组&#x1f4ad; 函数指针数组的定义&#x1f4ad; 函数指针数组的…...

专注:如何提高专注力和注意力的简要指南

专注力和集中力可能很难掌控的很好。大多数人都想学习如何提高注意力和注意力。但真的做到了&#xff1f;我们生活在一个嘈杂的世界里&#xff0c;不断的分心会使注意力难以集中。 此指南包含有关如何获得并保持专注的研究。我们将分解提升您的思维并关注重要事物背后的理论依…...

Linux查看内存的几种方法

PS的拼接方法 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head 进程的 status 比如说你要查看的进程pid是33123 cat /proc/33123/status VmRSS: 表示占用的物理内存 top PID&#xff1a;进程的ID USER&#xff1a;进程所有者 PR&#xff1a;进程的优先级别&#x…...

selenium定位rect元素

rect元素属性 rect元素的属性如下&#xff1a; x&#xff1a;此属性确定矩形的x坐标。 值类型&#xff1a;| ; 默认值&#xff1a;0 动画&#xff1a;是y&#xff1a;此属性确定矩形的y坐标。 值类型&#xff1a;| ; 默认值&#xff1a;0 动画&#xff1a;是width&#xff1a…...

uniapp <textarea>组件的踩坑

1、ios真机上&#xff0c;textarea输入时会触发页面其他点击事件&#xff0c; 解决方法&#xff1a;把textarea封装成基础组件&#xff0c;绕过这个bug。 2、使用auto-height属性&#xff0c;安卓真机上&#xff0c;会导致textarea高度异常&#xff0c; 官方人员解释&#xf…...

README.md 文档使用 treer 生成树形项目结构

一、前言 前后端编写 README.md 文档的时候&#xff0c;常常需要描写项目的结构&#xff0c;使用 tree 命令生成的目录又不能忽略某个目录&#xff0c;不方便。后来我找到了可以忽略某些目录的 treer命令 &#xff0c;特此记录一下: 二、使用 treer 生成项目结构 全局安装tr…...

朝花夕拾思维导图怎么画?看看这种绘制方法

朝花夕拾思维导图怎么画&#xff1f;绘制思维导图的好处有很多&#xff0c;首先它可以帮助人们更好地组织和管理知识&#xff0c;提高工作效率和学习效果。其次&#xff0c;绘制思维导图可以帮助人们更好地记忆知识点和理解知识点。总之&#xff0c;绘制思维导图可以帮助人们更…...

Python - OpenCV、OCR识别摄像头中的文字

使用Python3的OpenCV库来识别摄像头中的文字&#xff0c;以及使用OCR&#xff08;光学字符识别&#xff09;技术。 安装OpenCV库 在命令行中输入以下命令来安装OpenCV库&#xff1a; pip install opencv-python安装Tesseract OCR库 Tesseract OCR库是一种免费的光学字符识别…...

金融中的数学:贝叶斯公式

1.贝叶斯定理 贝叶斯定理是概率论中的一项重要定理&#xff0c;用于在已知某一事件的条件下&#xff0c;求另一事件发生的概率。它是根据条件概率推导出来的&#xff0c;得名于英国数学家托马斯贝叶斯。 贝叶斯定理可以表示为&#xff1a; 这个式子就是贝叶斯公式&#xff0c…...

ClickHouse单节点安装配置

四个安装包&#xff1a; 创建clickhouse目录 将安装包解压到该目录 tar -zxvf clickhouse-server-21.9.4.35.tgz -C /opt/module/clickhouse tar -zxvf clickhouse-common-static-21.9.4.35.tgz -C /opt/module/clickhouse tar -zxvf clickhouse-common-static-dbg-21.9.4.35.…...

AtcoderABC231场

A - Water PressureA - Water Pressure 题目大意 假设水压仅取决于深度&#xff0c;在深度为x米时&#xff0c;水压为x/100兆帕斯卡。 现在给定一个深度D米&#xff0c;需要计算在该深度下的水压是多少兆帕斯卡。 思路分析 直接将输入的深度除以100得到水压结果。 时间复杂…...

opengauss数据库快速安装

root执行 groupadd gs useradd -g gs gs passwd gs mkdir -p /opt/software/openGauss sudo chown gs:gs /opt/software/openGauss chmod 700 /opt/software/openGauss gs执行 上传文件到/home/gs目录下 tar -jxf openGauss-x.x.x-openEuler-64bit.tar.bz2 -C /opt/software/o…...

前端中的LocalStorage和SessionStorage

在前端开发中&#xff0c;常常需要在浏览器端保存数据&#xff0c;以便在页面刷新或跳转时能够保留数据或状态。在这种情况下&#xff0c;可以使用Web Storage API中的LocalStorage和SessionStorage来实现数据的存储。它们都是HTML5提供的本地存储解决方案&#xff0c;可以在浏…...

论文工具——写论文好用的绘图工具(甘特图+流程图+网络模型图+泳道图)

文章目录 引言正文手动画图的在线画图工具tldraw开源免费ProcessOnDraw.io 网络模型图工具NN-SVG设置参数自动生成Netron上传模型自动生成PlotNeuralNet编码生成 总结 引言 在写HiFi-GAN论文的代码阅读过程中&#xff0c;我发现仅仅通过文字来描述网络结构&#xff0c;不够详细…...

Vite构建的vue3项目修改网站标题和图标

1.准备一张.ico后缀的图片&#xff0c;这里推荐文件转换器&#xff0c;可以将常见的图片格式转为.ico格式图片。 2.修改网站标题和图标 网站的标题和图标都可以在项目根路径下的index.html下修改。 2.1 网站标题修改<title>标签体内容即可。 2.2 网站图标修改如图<…...

平安私人银行受邀慈善服务高质量发展会议,分享慈善规划服务

近日&#xff0c;中华慈善总会家风传承与慈善信托委员会成立仪式&#xff0c;以及由中华慈善总会、中国银行业协会联合发起的“慈善顾问赋能计划”启动仪式在北京举行。平安私人银行受邀参会并分享慈善规划服务&#xff0c;平安私人银行慈善业务总监王英及平安私人银行客户、“…...

MySQL主从复制、读写分离

一、前言二、主从复制原理2.1 MySQL复制类型2.2 MySQL主从复制工作过程2.3 MySQL的四种同步方式2.3.1 异步复制&#xff08;MySQL默认&#xff09;2.3.2 同步复制2.3.3 半同步复制&#xff08;企业常用&#xff09;2.3.4 增强半同步复制 2.4 MySQL主从复制延迟原因和优化方法2.…...

Redis配置与优化

目录 一、关系数据库与非关系型数据库 1、关系型数据库 2、非关系型数据库 3、关系型数据库和非关系型数据库区别 1、数据存储方式不同 2、扩展方式不同 3、对事务性的支持不同 二、Redis 1、简介 2、优点 3、缺点 4、使用场景 5、哪些数据适合放入缓存中 6、为什…...

leetCode刷题记录3-面试经典150题

文章目录 不要摆&#xff0c;没事干就刷题&#xff0c;只有好处&#xff0c;没有坏处&#xff0c;实在不行&#xff0c;看看竞赛题面试经典 150 题80. 删除有序数组中的重复项 II189. 轮转数组122. 买卖股票的最佳时机 II 不要摆&#xff0c;没事干就刷题&#xff0c;只有好处&…...

MySQL优化(面试)

文章目录 通信优化查询缓存语法解析及查询优化器查询优化器的策略 性能优化建议数据类型优化索引优化 优化关联查询优化limit分页对于varchar end mysql查询过程: 客户端向MySQL服务器发送一条查询请求服务器首先检查查询缓存&#xff0c;如果命中缓存&#xff0c;则立刻返回存…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...