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 文章目录一、继承…...

优化知识管理方法丨整理零碎信息,提高数据价值
信息流时代,知识成集合倍数增长,看似我们学习了很多知识,但知识零碎无系统,知识之间缺乏联系,没有深度,所以虽然你很努力,但你发现自己的能力增长特别缓慢,你需要整理知识将零散的知…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...

沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...