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

news2024/9/24 11:21:52

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《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;
}

📝全篇总结

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

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

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

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

相关文章

Android性能优化之游戏引擎初始化ANR

近期&#xff0c;着手对bugly上的anr 处理&#xff0c;记录下优化的方向。 借用网上的一张图&#xff1a; 这里的anr 问题是属于主线程的call 耗时操作。需要使用trace 来获取发生anr前一些列的耗时方法调用时间&#xff0c;再次梳理业务&#xff0c;才可能解决。 问题1 ja…

Java Servlet实现下拉选择查询(双表)和单列模式

0目录 1.Servlet实现下拉选择查询&#xff08;双表&#xff09; 2.单列模式 1.Servlet实现下拉选择查询&#xff08;双表&#xff09; 新建数据库和表 实体类 接口方法 实现接方法 Servlet类 Web.xml List.jsp 页面效果 加入功能 2.单列模…

批发零售进销存哪个好?盘点5款主流批发零售进销存软件!

在我看来&#xff0c;几乎没有批发零售行业不需要做进销存管理&#xff0c;哪怕是路边一个小摊贩&#xff0c;也需要做进销存管理&#xff0c;但是传统的进销存过程中存在很多问题&#xff1a; 前后方协作困难&#xff1a;采购/销售/财务工作相互独立&#xff0c;工作入口不一…

机器学习深度学习——多层感知机

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——感知机 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮助 上一节…

Java阶段五Day14

Java阶段五Day14 文章目录 Java阶段五Day14分布式事务整合demo案例中架构&#xff0c;代码关系发送半消息本地事务完成检查补偿购物车消费 鲁班周边环境调整前端启动介绍启动前端 直接启动的项目gateway&#xff08;网关&#xff09;login&#xff08;登录注册&#xff09;atta…

DSA之图(4):图的应用

文章目录 0 图的应用1 生成树1.1 无向图的生成树1.2 最小生成树1.2.1 构造最小生成树1.2.2 Prim算法构造最小生成树1.2.3 Kruskal算法构造最小生成树1.2.4 两种算法的比较 1.3 最短路径1.3.1 两点间最短路径1.3.2 某源点到其他各点最短路径1.3.3 Dijkstra1.3.4 Floyd 1.4 拓扑排…

数据库表结构导出成文档

1.需求说明 在系统交付的过程中&#xff0c;有时候需要交付数据库的表结构&#xff0c;如果系统做的比较大&#xff0c;比如几百张表的时候&#xff0c;靠人力一张表一张的写&#xff0c;那就是一个奔溃啊。所以今天特意找了一下工具&#xff0c;小巧安装。比较好用。 2.安装…

新型恶意软件DecoyDog正大规模入侵DNS

安全厂商 Infoblox 的调查研究显示&#xff0c;一个名为 DecoyDog&#xff08;诱饵狗&#xff09;的复杂恶意工具包通过域名系统&#xff08;DNS&#xff09;&#xff0c;从事网络间谍活动已达1年以上。 目前尚不清楚该恶意软件的幕后黑手是谁&#xff0c;但 Infoblox 的研究人…

通过REST API接口上传Nexus仓库

一、Nexus API文档 API文档链接&#xff1a;Components API 二、上传API接口说明 在Nexus中可以直接调试api接口&#xff0c;url参考&#xff1a;http://localhost:8081/#admin/system/api 三、上传请求案例 $ curl -X POST "http://localhost:8081/service/rest/v1/c…

营销系统积分数据库设计

营销系统总体数据-业务功能模型 在当今日益竞争的市场中&#xff0c;如何提高客户留存率和忠诚度&#xff0c;已成为各大企业迫切需要解决的问题。而积分商城/系统作为一种新型的营销方式&#xff0c;受到青睐。 积分商城/系统是指将用户在使用产品或服务时产生的积分&#xf…

2023扩散模型最新技术进展汇总

随着Stable Diffusion和Midjourney等图像生成模型的爆火&#xff0c;今年在模型领域&#xff0c;扩散模型稳占主导地位。因此&#xff0c;与之相关的新技术也层出不穷&#xff0c;短短1个月&#xff0c;扩散模型相关的论文就有上百篇了&#xff0c;可见其发展的火热趋势。 这次…

java注解@FeignClient修饰的类路径不在spring boot入口类所在的包下,有哪几种处理方式?

一、注解EnableFeignClients 修饰在spring boot入口类&#xff0c;使得openfeign的FeignClient注解生效。 我们进一步看看注解EnableFeignClients的使用方式。 String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<?>[] clie…

额外题目第1天|1365 941 1207 283 189 724 34 922 35 24

1365 暴力解法也能过 class Solution { public:vector<int> smallerNumbersThanCurrent(vector<int>& nums) {vector<int> result(nums.size(), 0);for (int i0; i<nums.size(); i) {int count 0;for (int j0; j<nums.size(); j) {if (nums[j]<…

互联网时代,企业经营管理面临的挑战有哪些?

随着科学技术的进步和社会经济的发展&#xff0c;大数据已经被应用到各个行业领域中&#xff0c;尤其在企业经营管理方面。通过对企业内部数据信息的利用&#xff0c;企业内部经营管理能力得到提升&#xff0c;那么在大数据时代&#xff0c;企业经营管理面临的挑战有哪些呢&…

C/C++几个关键知识点记录

1.将一个数值作为函数执行 (*(void(*)())0x13)();同理也可以将数值换成一个变量&#xff1a; int var0x13; (*(void(*)())var)();2.断言assert 用于判断输入的参数是否正确&#xff0c;实际就是判断输入的参数是否为0&#xff0c;如STM32的固件库中常用的assert_param()&…

Ceph入门到精通- Linux 磁盘管理(block 与 inode)

1 硬盘 block 与 inode 详解 1.1 Sector&#xff08;扇区&#xff09;与 Block&#xff08;块&#xff09; 1&#xff09; 硬盘的最小存储单位&#xff1a;sector&#xff08;扇区&#xff09;&#xff0c;每个扇区储存 512 字节&#xff1b;操作系统会一次性连续读取多个…

抖音短视频seo矩阵系统源码开发部署技术分享

抖音短视频的SEO矩阵系统是一个非常重要的部分&#xff0c;它可以帮助视频更好地被搜索引擎识别和推荐。以下是一些关于开发和部署抖音短视频SEO矩阵系统的技术分享&#xff1a; 一、 抖音短视频SEO矩阵系统的技术分享&#xff1a; 关键词研究&#xff1a;在开发抖音短视频SEO矩…

cvc-complex-type.2.4.a: 发现了以元素 ‘base-extension‘ 开头的无效内容。应以 ‘{layoutlib}‘ 之一开头。

✍️作者简介&#xff1a;沫小北&#xff08;专注于Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a;沫小北 CSDN、51cto博客、稀土掘金、简书、知乎、开源中国、博客园、慕课网 &#x1f310;系列专栏&#xff1a;码农小北 &#x1f514;如果文…

防止连点..

1.连点js文件 let timer; letflag /*** 节流原理&#xff1a;在一定时间内&#xff0c;只能触发一次** param {Function} func 要执行的回调函数* param {Number} wait 延时的时间* param {Boolean} immediate 是否立即执行* return null*/ function throttle(func, wait 500…

[Java] 单例设计模式详解

模式定义&#xff1a;保证一个类只有一个实例&#xff0c;并且提供一个全局访问点&#xff0c;时一种创建型模式 使用场景&#xff1a;重量级的对象&#xff0c;不需要多个实例&#xff0c;如线程池&#xff0c;数据库连接池 单例设计模式的实现 1.懒汉模式&#xff1a;延迟…