掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!
🍁博客主页:江池俊的博客
💫收录专栏:C语言进阶之路
💡代码仓库:江池俊的代码仓库
🎪我的社区:GeekHub
🎉欢迎大家点赞👍评论📝收藏⭐

文章目录
- 一、函数指针
- 代码1:
- 代码2:
- 二、函数指针数组
- 什么是函数指针数组?
- 为什么使用函数指针数组?
- 函数指针数组的基本用法
- 三、 指向函数指针数组的指针
- 指向函数指针数组的指针是什么?
- 为什么使用指向函数指针数组的指针?
- 总结
一、函数指针
在C语言中,函数是一等公民,可以像其他变量一样被传递和使用。而函数指针就是指向函数的指针变量,可以用来调用函数。本文将介绍函数指针的定义、使用方法以及注意事项。
函数指针的定义格式为:
返回值类型 (*指针变量名)(参数列表);
其中,返回值类型表示函数的返回值类型,指针变量名是指向函数的指针变量的名称,参数列表表示函数的参数类型和数量。
例如,定义一个指向返回值为int、参数为两个int类型的函数的指针:
int (*pAdd)(int, int);
这个指针可以指向任何返回值为int、参数为两个int类型的函数。
先看一段代码:
#include <stdio.h>
void test()
{printf("hehe\n");
}int main()
{printf("%p\n", test);printf("%p\n", &test);return 0;
}
输出结果:

输出的是两个地址,这两个地址是 test 函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:
void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
阅读两段有趣的代码:
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
这两段代码都涉及了函数指针的用法,让我们逐一来解释它们:
代码1:
(*(void (*)())0)();
这段代码是一个函数指针调用的例子。让我们逐步分解它:
void (*)()表示一个函数指针类型,它指向一个不接受任何参数(void),并且返回类型为void的函数。
(void (*)())0这里的(void (*)())实际上是一个强制转化的操作,它是将0强制转化为函数指针类型,即是将函数指针初始化为一个地址为 0 的空指针,也就是一个无效的函数指针。
(*(void (*)())0)();则是将这个无效的函数指针进行了间接调用,实际上是试图调用地址为 0 的函数,这通常会导致程序崩溃(因为操作系统不允许在地址 0 处执行代码,会触发段错误)。
这段代码在 C 语言中属于未定义行为,不应该在实际代码中使用,因为它可能导致程序的崩溃或其他不可预测的行为。
代码2:
void (*signal(int, void(*)(int)))(int);
//我们也可以将它简化为以下这种形式:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
这段代码涉及的是 C 语言中的信号处理函数
signal的声明。让我们逐步解释它:
void(*)(int)表示一个函数指针类型,它指向一个接受一个int参数并返回void的函数。
signal是一个函数,它接受两个参数:一个int参数和一个函数指针参数,然后返回一个与上述函数指针类型匹配的函数指针。
所以整个代码声明的含义是:
signal是一个函数,它接受一个int参数和一个函数指针参数,返回一个函数指针,该函数指针指向一个接受一个int参数并返回void的函数,这个函数通常用于处理信号。
这段代码通常用于在 C 语言中设置信号处理函数,以便在程序接收到特定信号时执行特定的操作。
请注意,这里只是声明了
signal函数的原型,实际使用时需要根据具体情况编写函数体。
注 :推荐《C陷阱和缺陷》
这本书中提及这两个代码。
二、函数指针数组
函数指针数组是 C 语言中一个强大且常用的工具,用于存储指向不同函数的指针,允许根据需要调用特定的函数。在本文中,我们将深入介绍函数指针数组的概念、用途和实例,帮助你理解并充分利用这一重要的 C 语言特性。
什么是函数指针数组?
数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组
比如:
int *arr[10];
//数组的每个元素是int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1先和[]结合,说明parr1是数组,数组的内容是什么呢?
是int (*)()类型的函数指针。
函数指针数组的用途:转移表
总之,函数指针数组实际上是一个数组,其元素都是指向函数的指针。这使得我们可以将不同的函数存储在数组中,并通过索引来调用特定的函数。这种灵活性使得函数指针数组在编写菜单驱动程序、状态机、回调机制等方面非常有用。以下是一个简单的示例:
#include <stdio.h>void func1()
{printf("调用 func1函数\n");
}void func2()
{printf("调用 func2函数\n");
}int main()
{void (*funcPtrArray[2])() = {func1, func2};//函数指针数组funcPtrArrayfuncPtrArray[0](); //通过函数指针数组调用 func1funcPtrArray[1](); //通过函数指针数组调用 func2return 0;
}
为什么使用函数指针数组?
函数指针数组在以下情况下非常有用:
-
菜单驱动程序: 当需要实现一个用户界面,允许用户从菜单中选择不同的操作时,函数指针数组可以用来存储每个操作的处理函数。
-
状态机: 在状态机的实现中,可以使用函数指针数组来存储每个状态的处理函数,从而实现状态转换时的操作。
-
回调机制: 当你需要在某个事件发生时调用不同的函数,比如事件处理、信号处理等,函数指针数组提供了一种简洁的方式。
-
动态选择算法: 如果你有多个算法实现,但在运行时决定使用哪一个算法,函数指针数组可以帮助你实现动态选择算法。
函数指针数组的基本用法
让我们通过一个简单的例子来演示函数指针数组的基本用法:实现一个简单的计算器,允许用户选择不同的操作。
例子:(计算机)
#include <stdio.h>int add(int a, int b)
{return a + b;
}int subtract(int a, int b)
{return a - b;
}int multiply(int a, int b)
{return a * b;
}int division(int a, int b)
{return a / b;
}int main()
{int x, y;int input = 1;int restult = 0;// 定义函数指针数组,存储不同的操作函数//转移表int(*operation[])(int x, int y) = { 0,add,subtract,multiply,division };while (input){printf("\n*************************\n");printf(" 1:add 2:subtract \n");printf(" 3:multiply 4:division \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("输入操作数:");scanf("%d %d", &x, &y);restult = (*operation[input])(x, y);// 调用选定的函数printf("restult = %d\n", restult);}elseprintf("输入有误,请重新输入\n");}return 0;
}
在上述示例中,我们定义了一个函数指针数组 operation,其中的元素分别指向 add、subtract 、 multiply和division 函数。用户可以根据选择来执行不同的操作。
三、 指向函数指针数组的指针
指向函数指针数组的指针是什么?
指向函数指针数组的指针是一个指针,指针指向一个的数组,数组元素都是函数指针。这种指针提供了对函数指针数组的更高级别的访问方式,使我们能够更灵活地处理函数指针数组。以下是一个示例:
#include <stdio.h>void func1()
{printf("调用 func1函数\n");
}void func2()
{printf("调用 func2函数\n");
}int main()
{void (*funcPtrArray[2])() = {func1, func2};//函数指针的数组funcPtrArrayvoid (*(*ptrToFuncPtrArray))() = funcPtrArray;//指向函数指针数组funcPtrArray的指针ptrToFuncPtrArrayptrToFuncPtrArray[0](); // 通过指向函数指针数组的指针调用 func1ptrToFuncPtrArray[1](); // 通过指向函数指针数组的指针调用 func2return 0;
}
为什么使用指向函数指针数组的指针?
指向函数指针数组的指针可能在日常编程中不常见,但在某些情况下非常有用。以下是一些使用情况:
-
函数指针数组的参数传递: 通过传递指向函数指针数组的指针作为参数,可以避免复制整个数组,从而提高效率。
-
动态函数调用: 使用指向函数指针数组的指针,可以在运行时根据条件选择不同的函数进行调用。
-
代码模块化: 当函数指针数组较大或需要在多个函数之间共享时,使用指向函数指针数组的指针可以提高代码的模块化性。
-
函数指针数组的排序: 可以使用指向函数指针数组的指针来执行对函数指针数组的排序操作,以实现按照某种规则调用函数。
总结
- 在本篇博客中,我们深入探讨了 C语言中的三个重要概念:函数指针、函数指针数组和指向函数指针数组的指针。这些概念虽然可能听起来有些复杂,但它们为我们在C编程中提供了更大的灵活性和功能。
- 函数指针允许我们将函数作为数据,传递给其他函数或存储在数据结构中。通过使用函数指针,我们可以实现更动态和可配置的程序设计,同时避免代码的重复。
- 函数指针数组进一步扩展了这种灵活性,允许我们将多个函数指针组织在一个数组中,以便在运行时根据需要选择和调用不同的函数。这在构建可插拔的模块和实现动态行为时特别有用。
- 最后,我们介绍了指向函数指针数组的指针,这为我们提供了一种更高级的访问方式,使得处理函数指针数组变得更加优雅。它可以应用于参数传递、动态函数调用、代码模块化以及对函数指针数组的排序等各种场景,从而增强了程序的模块性、可维护性和性能。
🔥今天的分享就到这里, 如果觉得博主的文章还不错的话, 请👍三连支持一下博主哦🤞

相关文章:
掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!
🍁博客主页:江池俊的博客 💫收录专栏:C语言进阶之路 💡代码仓库:江池俊的代码仓库 🎪我的社区:GeekHub 🎉欢迎大家点赞👍评论📝收藏⭐ 文章目录 一…...
在Docker上部署2台节点,利用Keeplived实现双节点VIP 高可用,不需要关闭Keeplived,实现vip来回切换。
前言: keeplived的做高可用网上有很多例子,但是都存在这样那样的问题,比如: 1.使用的是默认抢占式,这样在主节点恢复后,又会将VIP 漂移回到主节点上,因此需要使用非抢占式模式,故障恢复时,可避免 VIP 切换造成的服务延迟。 2.使用的是默认组播,信息都会向默认的224.0.…...
leetcode 279. 完全平方数
2023.8.18 与零钱兑换相似,本题属于完全背包问题:完全平方数为物品,整数n为背包。 直接上代码: class Solution { public:int numSquares(int n) {vector<int> dp(n1 , INT_MAX);dp[0] 0;for(int i1; i*i<n; i){for(in…...
【从零学习python 】48.Python中的继承与多继承详解
文章目录 在Python中,继承可以分为单继承、多继承和多层继承。单继承 继承语法多继承 语法格式使用多继承时需要注意以下事项Python中的MRO新式类和旧式(经典)类 进阶案例 在Python中,继承可以分为单继承、多继承和多层继承。 单…...
二、编写第一个 Spring MVC 程序(总结项目报 404 问题以及 Spring MVC 的执行流程)
文章目录 一、编写第一个 Spring MVC 程序二、项目运行时报 404错误原因总结三、Spring MVC 的执行流程 一、编写第一个 Spring MVC 程序 创建 maven 项目,以此项目为父项目,在父项目的 pom.xml 中导入相关依赖 <dependencies><dependency…...
okhttp源码简单流程分析
拦截器详细解析可以看大佬简书 "https://www.jianshu.com/p/6fac73f7570f"和 “https://www.jianshu.com/p/3c740829475c” okhttp请求流程 1:OkHttpClient okHttpClient new OkHttpClient.Builder() 构建一个okhttpClient对象,传入你想传入的…...
SpringBoot整合Shiro实现登录认证,鉴权授权
文章目录 前言一、shiro简介二、环境搭建2.1.数据库2.1.1user用户表2.1.2user_role用户角色关系表2.1.3role角色表2.1.4role_permission角色权限关系表2.1.5permission权限表 2.2导坐标2.3实体类2.3.1User2.3.2Role2.3.3Permission 2.4MVC三层2.4.1User2.4.1.1mapper层2.4.1.2s…...
Airbnb开源数据可视化工具Visx
一、什么是visx visx 是用于 React 的富有表现力的底层可视化组件集合,结合了 d3 的强大功能来生成可视化,以及 React 更新 DOM 的诸多优势。 在 Airbnb 内部,visx 的目标是统一整个公司的可视化堆栈,在此过程中,创建了 visx 项目,从而有效的将 D3 的强大功能与 React …...
VR仿真实训系统编辑平台赋予老师更多自由和灵活性
为了降低院校教师在VR虚拟现实方面应用的门槛,VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验,制作了VR动物课件编辑器,正在逐渐受到师生们的关注和应用。 简单来说,VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…...
父类对象转成子类对象
import org.springframework.beans.BeanUtils; import java.util.ArrayList; import java.util.List; public class Test {public static void main(String[] args) {List<B> bList new ArrayList<>();B b new B("a","这是a","b",…...
Spring Boot中如何使用Flyway进行数据库迁移
在本文中,我们将了解如何使用 Flyway 来管理 Spring Boot 应用程序中的 SQL 数据库架构。 在本文中,我们将了解如何使用 Flyway 来管理Spring Boot应用程序中的SQL 数据库架构。 Flyway是一个数据库迁移工具,它提供迁移历史和回滚的功能&…...
web在线编辑器(vue版)
目录 前言一、monaco-editor1、源码2、体积优化 二、ace-editor?1、源码2、体积优化 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多…...
【论文阅读】 Model Sparsity Can Simplify Machine Unlearning
Model Sparsity Can Simplify Machine Unlearning 背景主要内容Contribution Ⅰ:对Machine Unlearning的一个全面的理解Contribution Ⅱ:说明model sparsity对Machine Unlearning的好处Pruning方法的选择sparse-aware的unlearning framework Experiments…...
Spring Clould 部署 - Docker
视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) 初识Docker-什么是Docker(P42,P43) 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&…...
linux--链表动态创建
头插法: 核心代码: s->next head->next; head->next s; 尾插法 核心代码: tail head; s->next NULL; tail->next s; tail s; 当用头插法依次插入值分别为1,2,3,4,5的结点后, 单链表顺序为: he…...
iBooker 布客技术评论 20230818
一、程序员自检手册 为了避免焦虑,你首先需要做的就是梳理你的业务: (1)你所在的行业是轻资产还是重资产? 重资产就是人绕着机器转,创业需要买一大堆设备。如果是重资产,赶紧换一个。 &…...
CK-A60180、CK-B1542、CK-L3095单向离合器
CK-A1542、CK-A1747、CK-A2052、CK-A2652、CK-A3072、CK-A3580、CK-A4090、CK-A45100、CK-A450110、CK-A60130、CK-A65140、CK-A70150、CK-A75160、CK-A80170、CK-A1250、CK-A1855、CK-A2060、CK-A2563、CK-A2563T、CK-A2870、CK-A3080T、CK-A3585、CK-A35100、CK-A35140、CK-A…...
单因素多变量方差分析
多变量方差分析:是对多个独立变量是否受单个或多个因素影响而进行的方差分析。它不仅能够分析多个因素对观测变量的独立影响,更能够分析多个因素的交互作用能否对观测变量产生影响。本章以单因素多变量分析为例,即一个分组变量和多个欲分析的…...
Python Web:Django、Flask和FastAPI框架对比
原文:百度安全验证 Django、Flask和FastAPI是Python Web框架中的三个主要代表。这些框架都有着各自的优点和缺点,适合不同类型和规模的应用程序。 1. Django: Django是一个全功能的Web框架,它提供了很多内置的应用程序和工具&am…...
【CI/CD】Rancher K8s
Rancher & K8s Rancher 和 K8s 的关系是什么?K8s 全称为 Kubernetes,它是一个开源的,用于管理云平台中多个主机上的容器化的应用。而 Rancher 是一个完全开源的企业级多集群 Kubernetes 管理平台,实现了 Kubernetes 集群在混合…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
