C语言--指针进阶2
目录
- 前言
- 函数指针
- 函数指针数组
- 指向函数指针数组的指针
- 回调函数
前言
本篇文章我们将继续学习指针进阶的有关内容
函数指针
我们依然用类比的方法1来理解函数指针这一全新的概念,如图1

我们用一段代码来验证一下:
int Add(int x, int y)
{return x+y;
}int main()
{printf("%p\n", &Add);printf("%p\n", Add);return 0;
}
打印结果如图2

进一步验证了函数指针确实是存放函数的地址。
值得注意的是,函数名和取地址函数名的结果是一样的,这有别于数组名和取地址数组名
那么如果我们想用一个指针变量来存放函数的地址该怎么书写呢?
同样是类比数组指针的写法,如下:
int (*pf)(int,int) = Add;
这里的pf就是函数指针,在书写的时候只用交代类型即可(int char float等),不需要把形参也写进去。
如果我们想通过函数指针调用这个函数怎么书写呢?
如下代码:
int Add(int x, int y)
{return x+y;
}int main()
{int (*pf)(int, int) = Add;printf("%d\n", (*pf)(3, 5));printf("%d\n", pf(3, 5));printf("%d\n", Add(3, 5));return 0;
}
打印结果如图3

所以以上三种形式的书写均可实现函数的调用。
来看两段有趣的代码
先来看第一个:
(*(void (*)())0)();
对于这样复杂的代码,我们来逐步地分析:
1,将0强制类型转换为void (*)() 类型的函数指针。
2,这就意味着0地址处放着一个函数,函数没有参数,返回类型是void。
3,调用0地址处对这个函数。
我们再来看第二个:
void (*signal(int , void(*)(int)))(int);
我们同样来逐步分析:(注意这里的signal并没有和结合)
1,上述的代码是一个函数的声明。
2,函数的名字是signal。
3,函数的参数第一个是int,第二个是void( )(int)类型的函数指针。
4,该函数指针指向的函数参数是int,返回类型是void。
5,signal函数的返回类型也是一个函数指针。
6,该函数指针指向的函数参数是int,返回类型是void。
这样讲可能还是不好理解,我们再对代码进行一下简化:
typedef int* ptr_t;
typedef void(*pf_t)(int);//将void(*)(int)类型重新起个别名pf_t
int main()
{void(* signal(int,void(*)(int)))(int);pf_t signal(int,pf_t);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 (*pf[4])(int, int) = { Add,Sub,Mul,Div };int i = 0;for (i = 0; i < 4; i++){int ret = pf[i](8, 4);printf("%d\n", ret);}return 0;
}
打印结果如图4

那么函数指针数组有什么作用呢?
我们可以通过函数指针数组来实现一个简单的计算器:
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;int (*pfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };do{menu();printf("请选择: >");scanf_s("%d", &input);if (input == 0){printf("退出计算器\n");break;}if (input >= 1 && input <= 4){printf("请输入两个操作数:>");scanf_s("%d %d", &x, &y);ret = pfArr[input](x, y);printf("结果为%d\n", ret);}} while (input);return 0;
}
运行效果如图5

这样我们就通过灵活使用函数指针数组,巧妙的简化了代码,防止冗长。
指向函数指针数组的指针
指向函数指针数组的指针的书写方式如下
int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}
int main()
{int (*pf)(int, int) = Add;int (*pfArr[4])(int, int) = { Add,Sub };int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针变量return 0;
}
我们分步来理解这个式子
1,ppfArr是一个指针变量。
2,该指针变量指向的是一个数组,有四个元素。
3,该数组的每个元素类型是int ( )(int,int),是一个函数指针。
回调函数
我们先来看概念:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
那么回调函数具体怎么使用呢?看下面这段代码
int main()
{int input = 0;int x, y;int ret = 0;scanf_s("%d", &input);switch (input){case 1:printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);case 2://Subcase 3://Mulcase 4://Div}return 0;
}
我们会发现,case等于不同的数时,总会执行重复的语句。我们能不能这样思考:假设我们把这些重复的语句封装成一个函数,然后把不同运算的函数地址转过去调用呢?
我们定义一个Calc函数:
void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf_s("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);}
这样我们就实现了在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应的这样一个效果(即case等于不同的值是执行不同的响应)。
以上就是本章全部内容,下一章我们将运用回调函数的特性来模拟实现库函数–qsort(快速排序)。
相关文章:
C语言--指针进阶2
目录前言函数指针函数指针数组指向函数指针数组的指针回调函数前言 本篇文章我们将继续学习指针进阶的有关内容 函数指针 我们依然用类比的方法1来理解函数指针这一全新的概念,如图1 我们用一段代码来验证一下: int Add(int x, int y) {return xy;…...
【步进电机和 Arduino】
【步进电机和 Arduino】 前言1. 什么是步进电机及其工作原理?1.1 步进电机结构1.2 绕线方式1.3 通电方式2. 如何使用Arduino和A17步进驱动器控制NEMA4988步进电机2.1 A4988 和 Arduino 连接2.2 测量AB相2.3 A4988 限流3. 步进电机和 Arduino3.1 示例代码 13.2 示例代码 24. 使…...
【面试一:|和||、和区别】
相同点: ||和&&都是逻辑运算符,而|和&是位运算符。位运算符的优先级要比逻辑运算符的优先级高。 &和&&的区别 &和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运…...
【一天一门编程语言】使用汇编语言实现斐波那契数列
文章目录使用汇编语言实现斐波那契数列一、什么是斐波那契数列二、如何用汇编语言实现斐波那契数列一、汇编语言概念1.1 什么是汇编语言1.2 汇编语言的特点二、汇编语言指令2.1 简单指令2.2 复杂指令汇编语言程序结构代码实例指令集常用指令指令代码实例使用汇编语言实现斐波那…...
RabbitMQ实现死信队列
目录死信队列是什么怎样实现一个死信队列说明实现过程导入依赖添加配置编写mq配置类添加业务队列的消费者添加死信队列的消费者添加消息发送者添加消息测试类测试死信队列的应用场景总结死信队列是什么 “死信”是RabbitMQ中的一种消息机制,当你在消费消息时&#…...
【Linux】安装Tomcat教程
目录 1.上传安装包 2.解压安装包 3.启动Tomcat 4.查看启动日志 5.查看进程 6.开放端口 7.停止Tomcat 1.上传安装包 使用FinalShell自带的上传工具将Tomcat的二进制发布包上传到Linux(与前面上传JDK安装包步骤 一致)。 2.解压安装包 将上传上来的安装包解压到指定目录…...
学习笔记之Vuex(五)
Vuex(五)Vuex一、什么是Vuex二、Vuex工作原理三、搭建Vuex环境四、求和案例分析4.1 求和案例——vue实现4.2 求和案例——vuex实现(五)Vuex 一、什么是Vuex 1.概念 在Vue中实现集中式状态(数据)管理的一…...
SSM知识快速复习
SSM知识快速复习SpringIOCDIIOC容器在Spring中的实现常用注解Autowired注解的原理AOP相关术语作用动态代理实现原理事务Transactional事务属性:只读事务属性:超时事务属性:回滚策略事务属性:事务隔离级别事务属性:事务…...
【Linux】安装MySQL
目录 1.检测当前系统是否安装过MySQL相关数据库 2. 卸载现有的MySQL数据库 3.上传解压 4.顺序安装rpm包 5.启动MySQL 6.查看临时密码 7.登录MySQL 8.开放端口 1.检测当前系统是否安装过MySQL相关数据库 需要通过rpm相关指令,来查询当前系统中是否存在已安…...
【深度学习】手把手教你开发自己的深度学习模板
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言1数据相关1.1 数据初探1.2.数据处理1.3 数据变形2 定义网络,优化函数3. 训练前言 入坑2年后,重新梳理之前的知识,发现其实需…...
一个诡异的 Pulsar InterruptedException 异常
背景 今天收到业务团队反馈线上有个应用往 Pulsar 中发送消息失败了,经过日志查看得知是发送消息时候抛出了 java.lang.InterruptedException 异常。 和业务沟通后得知是在一个 gRPC 接口中触发的消息发送,大约持续了半个小时的异常后便恢复正常了&…...
Java岗面试题--Java并发(volatile 专题)
目录1. 面试题一:谈谈 volatile 的使用及其原理补充:内存屏障volatile 的原理2. 面试题二:volatile 为什么不能保证原子性3. 面试题三:volatile 的内存语义4. 面试题四:volatile 的实现机制5. 面试题五:vol…...
Java---打家劫舍ⅠⅡ
目录 打家劫舍Ⅰ 题目分析 代码一 代码二 打家劫舍Ⅱ 打家劫舍Ⅰ 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被…...
MySQL Lesson4
1:关于查询结果集的去重(distinct) select distinct job from emp; **distinct只能出现在所有字段的最前面。所表示的含有是所有的结果联合起来去重。 select distinct deptno,job from emp order by deptno; select count(distinct job)from…...
浅谈权限获取方法之文件上传
概述 文件上传漏洞是发生在有上传功能的应用中,如果应用程序对用户的上传文件没有控制或者存在缺陷,攻击者可以利用应用上传功能存在的缺陷,上传木马、病毒等有危害的文件到服务器上面,控制服务器。 漏洞成因及危害 文件上传漏…...
资产设备防拆标签安全防护和资产定位解决方案
随着社会经济的发展和高新技术的日新月异,对各方面的安全要求也在不断地提高,以物联网安防、入侵报警和出入口控制、应急系统等为主的安全防范系统日益成为各类文物场所智能化弱电工程不可或缺的组成部分,是重点资产管理场所内加强管理和安全…...
企业电子招标采购源码之电子招标投标全流程!
随着各级政府部门的大力推进,以及国内互联网的建设,电子招投标已经逐渐成为国内主流的招标投标方式,但是依然有很多人对电子招投标的流程不够了解,在具体操作上存在困难。虽然各个交易平台的招标投标在线操作会略有不同࿰…...
【考研408】计算机网络笔记
文章目录计算机网络体系结构计算机网络概述计算机网络的组成计算机网络的功能计算机网络的分类计算机网络的性能指标课后习题计算机网络体系结构与参考模型计算机网络协议、接口、服务的概念ISO/OSI参考模型和TCP/IP模型课后习题物理层通信基础基本概念奈奎斯特定理与香农定理编…...
[C++]继承
🥁作者: 华丞臧 📕专栏:【C】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉LeetCode 文章目录一、继承…...
优化知识管理方法丨整理零碎信息,提高数据价值
信息流时代,知识成集合倍数增长,看似我们学习了很多知识,但知识零碎无系统,知识之间缺乏联系,没有深度,所以虽然你很努力,但你发现自己的能力增长特别缓慢,你需要整理知识将零散的知…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...
41道Django高频题整理(附答案背诵版)
解释一下 Django 和 Tornado 的关系? Django和Tornado都是Python的web框架,但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。它遵循MVC设计,并强调代码复用。Django有…...
