C语言教程——指针进阶(2)
目录
一、函数指针数组
1.1函数指针数组写法
1.2函数指针用途
二、指向函数指针数组的指针
2.1概念
三、回调函数
3.1用法
3.2qsort排序
总结
前言
我们接着上一篇的函数指针往下学习。
一、函数指针数组
1.1函数指针数组写法
我们都知道指针数组,里面可以放字符指针,或者整形指针,例如:
char* arr[6];//字符指针数组int* arr[6];//整形指针数组
那么我们就可以想一想函数指针数组是否可以呢? 当然可以,写函数指针数组,需要在函数指针这个基础上进行改造:
int(*str)(const char*) = &my_strlen;int(*str[5])(const char*);
第一行 是一个函数指针,我们将my_strlen这个函数的地址赋予给它,当我们想要把前面的函数指针改成函数指针就可以在里面加上数组元素个数,其实就是在这个函数指针后面加上一个方括号再确定元素个数就可以了。
int(*)(const char*);
我们去掉数组名和元素,剩下的就是一个函数指针,所以就是这个数组里面存放的就是这个函数的地址。我们就可以赋值,传入地址:
int(*str[5])(const char*)={ &my_strlen,.....}
1.2函数指针用途
我们可以用一个简单的可以实现整数加减乘除的计算器。
先实现一个简单的菜单和基本逻辑的实现,重点实际是函数的编写和函数的调用(VS2022):
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void menu()
{printf("********************************************\n");printf("*********** 1.add 2.sub ***********\n");printf("*********** ***********\n");printf("*********** 3.mul 4.div ***********\n");printf("*********** 0.exit ***********\n");printf("********************************************\n");}int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:break;case 2:break;case 3:break;case 4:break;case 0:printf("退出计算器\n");break;default:printf("选择错误\n");} } while (input);return 0;
}
这里规定的是选择1,2,3,4,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("请输入两个数:>");scanf("%d %d", &x, &y);ret=Add(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");} } while (input);return 0;
}
这样一个简单的计算器就写好了,我们可以运行并且测试。但这只是这几种简单的运算,当我们如果想要拓展其它的算数方法,那么函数要写,switch里面的case也要写,那么里面就会变得非常长,这时候我们就可以优化一下。
我们知道函数的调用参数和返回值都一样,所以可以写上函数指针数组,存放函数的地址,这种叫做转移表:
int (*pf[5])(int,int) = { NULL,Add,Sub,Mul,Div };
我们这里用上一个名字为pf的函数指针数组来接受这些函数的地址,每一个函数对应的下标就是菜单上规定的数字。
这时候我们就不需要用switch语句了,我们想要选择几就访问下标为几的位置。这时候我们改一下主函数:
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("请选择:>");scanf("%d", &input);if (input == 0){printf("退出计算器\n");break;}else if (input >= 1 && input <= 4){printf("请输入两个数:>");scanf("%d %d", &x, &y);ret = pf[input](x, y);printf("结果是: %d\n", ret);}else{printf("选择错误\n");}} while (input);return 0;
}
这样未来我们想要添加功能,就只需要加上新函数的编写,剩下的只需要改一改限制条件就可以,其他都不用改动,大大改善了switch的代码量。但这个上述代码只适用两个整数之间的操作(双目操作符的运算)。
函数指针数组在动态调用函数、简化代码逻辑、扩展性和灵活性以及实现回调函数等方面具有很高的实用价值,是一种常用的编程技术。
二、指向函数指针数组的指针
2.1概念
int(*pf[5])(int,int);
ppf=&pf;
如上述代码,pf是一个存放函数指针的一个五个元素的数组,而ppf指向这个数组,所以就是指向函数指针数组的指针。
int(*(*ppf)[5])(int,int);
int(*)(int,int);
(*ppf)代表是一个指针,(*ppf)[5]代表指向的是一个五个元素的数组,而int(*)(int,int)代表是一个函数指针,所以数组里存放的是函数指针,这个数组也就是一个函数指针数组,所以ppf就是一个指向函数指针数组的指针;
三、回调函数
回调函数就是一个通过函数指针调用的函数,如果把函数的指针(地址)作为一个参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,回调函数b不是由该函数的实现方法直接调用,而是在特定的事件或条件发生由另外一方调用的,对于该事件或条件进行影响。
也就是用函数指针调用函数。
3.1用法
之前的switch,,,case语句是这样的:
case 1:printf("请输入两个数:>");scanf("%d %d", &x, &y);ret=Add(x, y);printf("结果是: %d\n", ret);break;case 2:printf("请输入两个数:>");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("结果是: %d\n", ret);break;
我们可以看到很多都是一样的代码,那可不可以把一样的代码放在一个函数里面调用呢:
void func()
{printf("请输入两个数:>");scanf("%d %d", &x, &y);ret=Add(x, y);printf("结果是: %d\n", ret);
}case 1:func();break;
case 2:func();break;
这里就有一个问题,这里两次case都是计算加法,这时候我们就可以当我们case 1 的时候,将加法的地址传到函数中,当case 2 的时候,把减法的地址传过去:
void func(int(*p)(int,int))
{printf("请输入两个数:>");scanf("%d %d", &x, &y);ret=p(x, y);printf("结果是: %d\n", ret);
}case 1:func(Add);break;
case 2:func(Sub);break;
把函数的参数写成一个函数指针就可以,调用的时候就用指针名和传参就可以实现了。这里通过p来调用这些函数就叫做回调函数。
qsort函数就是一个典型的例子。
3.2qsort排序
这里先介绍一下这个函数:
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
这里传入的参数分别是是要排序的目标(起始位置),排序的个数,单个的大小,还有比较函数。这里的比较函数是需要自己进行编写的,并且比较函数是返回的一个数。
int compareMyType (const void * a, const void * b)
{if ( *(MyType*)a < *(MyType*)b ) return -1;if ( *(MyType*)a == *(MyType*)b ) return 0;if ( *(MyType*)a > *(MyType*)b ) return 1;
}

这里是官方给出的代码,实际我们只需要返回一个值就可以。
下面是通过qsort函数来排序整数数组还有排序结构体:
给定一个整数数组,进行排序
//基于快速排序的stdlib库里的qsort进行排序#include<stdio.h>
#include<stdlib.h>int comper(const void*e1, const void*e2)
{return *(int*)e1 - *(int*)e2;
}int main()
{int arr[5] = { 4,6,2,3,1 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), comper);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}
基于结构体按照年龄和名字来进行排序:
//结构体排序
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct Stu
{char name[20];int age;
};
//年龄排序
int comper(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}//按照名字来排序
int comper1(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name , ((struct Stu*)e2)->name) ;//按照字典序进行排序
}void test2()
{struct Stu s[3] = { {"zhangsan",30},{"lisi",50},{"wanghu",33} };int sz = sizeof(s) / sizeof(s[0]);qsort(s,sz,sizeof(s[0]),comper1);for (int i = 0; i < sz; i++){printf("%s %d\n", s[i].name, s[i].age);}
}int main()
{test2();return 0;
}
总结
这里进行了函数指针数组的用法和写法,回调函数sqsort等的知识
相关文章:
C语言教程——指针进阶(2)
目录 一、函数指针数组 1.1函数指针数组写法 1.2函数指针用途 二、指向函数指针数组的指针 2.1概念 三、回调函数 3.1用法 3.2qsort排序 总结 前言 我们接着上一篇的函数指针往下学习。 一、函数指针数组 1.1函数指针数组写法 我们都知道指针数组,里面可以…...
调和级数不为整数的证明
文章目录 1. 问题引入2. 证明2.1 引理12.2 引理22.3 引理3:2.4 核心证明: 3. 参考 1. 问题引入 s ( n ) 1 1 2 1 3 ⋯ 1 n , n ∈ N ∗ , n ≥ 2 s(n) 1\frac{1}{2}\frac{1}{3}\cdots\frac{1}{n}, \quad \\n \in N^*, n \ge2 s(n)12131⋯n1,…...
基于微信小程序的在线学习系统springboot+论文源码调试讲解
第4章 系统设计 一个成功设计的系统在内容上必定是丰富的,在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值,吸引更多的访问者访问系统,以及让来访用户可以花费更多时间停留在系统上,则表明该系统设计得比较专…...
基于 Boost.Asio 和 Boost.Beast 的异步 HTTP 服务器(学习记录)
已完成功能: 支持 GET 和 POST 请求的路由与回调处理。 解析URL请求。 单例模式 管理核心业务逻辑。 异步 I/O 技术和 定时器 控制超时。 通过回调函数注册机制,可以灵活地为不同的 URL 路由注册处理函数。 1. 项目背景 1.1 项目简介 本项目是一个基于…...
有机物谱图信息的速查技巧有哪些?
谱图信息是化学家解读分子世界的“语言”,它们在化学研究的各个领域都发挥着不可或缺的作用。它们是理解和确定分子结构的关键,对化学家来说极为重要,每一种谱学技术都提供了不同的视角来观察分子,从而揭示其独特的化学和物理特性…...
Eureka缓存机制
一、Eureka的CAP特性 Eureka是一个AP系统,它优先保证可用性(A)和分区容错性(P),而不保证强一致性(C)。这种设计使得Eureka在分布式系统中能够应对各种故障和分区情况,保…...
【LC】78. 子集
题目描述: 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出࿱…...
协同过滤算法私人诊所系统|Java|SpringBoot|VUE|
【技术栈】 1⃣️:架构: B/S、MVC 2⃣️:系统环境:Windowsh/Mac 3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7 4⃣️:技术栈:Java、Mysql、SpringBoot、Mybatis-Plus、VUE、jquery,html 5⃣️…...
Docker部署Naocs-- 超细教程
Docker 拉取镜像 docker pull nacos/nacos-server:v2.2.0 挂载目录 如果不是root账号 前面加sudo 或者 切换root账号 su root(命令) mkdir -p /mydata/nacos/logs/ #新建logs目录 mkdir -p /mydata/nacos/conf/ #新建conf目录 启动容器…...
[java基础-集合篇]优先队列PriorityQueue结构与源码解析
优先队列PriorityQueue 优先级队列表示为平衡二进制堆: queue[n] 的两个子级是 queue[2*n1] 和 queue[2*(n1)]。 注:左子节点index2*parentIndex1,右子节点index2*parentIndex2,源码中计算parent位置时就是这样反过来计算的 优…...
12. C语言 数组与指针(深入理解)
本章目录: 前言1. 什么是数组?2. 数组的声明与初始化声明数组初始化数组 3. 访问数组元素遍历数组 4. 获取数组长度使用 sizeof 获取长度使用宏定义简化 5. 数组与指针数组名与指针的区别使用指针操作数组 6. 多维数组遍历多维数组 7. 数组作为函数参数8. 高级技巧与…...
Postman接口测试基本操作
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 Postman-获取验证码 需求:使用Postman访问验证码接口,并查看响应结果。 地址:http://kdtx-test.itheima.net/api/captchaIm…...
MySQL--2.1MySQL的六种日志文件
大家好,我们来说一下MySQL的6中日志文件。 1.查询日志 查询日志主要记录mysql的select查询的,改配置是默认关闭的。不推荐开启,因为会导致大量查询日志文件储存占用你的空间。 举例查询一下 select * from class; 开启查询日志的命…...
spring task使用
Spring Task 简介 Spring Task 是 Spring 框架原生自带的任务调度框架,它犹如一把瑞士军刀,为开发者提供了丰富多样的功能,助力轻松创建和管理定时任务。相较于其他一些第三方任务调度框架,Spring Task 最大的优势在于其与 Sprin…...
【FPGA】时序约束与分析
设计约束 设计约束所处环节: 约束输入 分析实现结果 设计优化 设计约束分类: 物理约束:I/O接口约束(例如引脚分配、电平标准设定等物理属性的约束)、布局约束、布线约束以及配置约束 时序约束:设计FP…...
LLM的MoE由什么构成:门控网络,专家网络
LLM的MoE由什么构成:门控网络,专家网络 目录 LLM的MoE由什么构成:门控网络,专家网络专家网络门控网络MoE在联邦学习中的使用及原理专家网络 定义与特点:是一组独立的模型,每个模型都负责处理某个特定的子任务或学习输入空间的特定部分。这些专家可以是简单的线性回归模型…...
HTML-多媒体标签
除了图像,网页还可以放置视频和音频。 1.<video> <video>标签是一个块级元素,用于放置视频。如果浏览器支持加载的视频格式,就会显示一个播放器,否则显示<video>内部的子元素。 <video src"example.…...
MySQL笔记大总结20250108
Day2 1.where (1)关系运算符 select * from info where id>1; select * from info where id1; select * from info where id>1; select * from info where id!1;(2)逻辑运算符 select * from info where name"吴佩奇" and age19; select * from info wh…...
stm32week3
stm32学习 二.外设 8.TIM输出比较 OC(output compare)输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0、翻转操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道 高级定时器的…...
uniapp 的uni.getRecorderManager() 录音功能小记
官网上明确说的是全局唯一并且只是获取对象,所以会导致一个问题就是,当你多个页面要用到这个对象的时候,会发现 onStop 方法会被覆盖,导致调用结果不是自己想要的 解决办法也简单粗暴,在需要用到的界面重新覆盖onStop…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
