掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!
🍁博客主页:江池俊的博客
💫收录专栏: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 集群在混合…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...