那些 C语言指针 你不知道的小秘密 (完结篇)

本篇会加入个人的所谓‘鱼式疯言’
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
在这里插入图片描述

前言

在上一篇指针小秘密的文章中,我们主要理解了一下几点

  • 字符指针 :分别带友友们了解了 单字符 和 字符串 的所对应的字符指针以及使用细节
  • 数组指针 : 介绍了数组指针的概念 和 以及 数组指针 常用场合
  • 二维数组传参的本质 : 理解了二维数组本质的 内核

今天小编将带着大家给我们C语言最重要的指针内容画上一个完美的句号,这次主要的指针主题是咱们的函数指针,下面让我们最后享受我们魔术般的指针秘密吧 💕 💕 💕

目录

  1. 函数指针
  2. 函数指针数组
  3. 函数指针数组的实际运用:转移表
  4. 函数指针的实际运用:回调函数

一. 函数指针

这时就有小伙伴问了,什么 😲 😲 😲
函数居然还有指针 ! ! !

是的,我们函数也有自己专门的地址,而且可以通过指针变量来存储的自身函数的地址的

<1>. 函数指针的简介

C语言中的函数指针是指向函数的指针 变量 。它可以用来存储函数的地址,以便在程序中 调用 该函数。

函数指针的声明格式如下:

  return_type (*pointer_name)(parameter_list);
  • return_type是函数的返回类型
  • pointer_name是函数指针的名称
  • parameter_list是函数的参数列表。

<2>. 举个栗子

小编在上面提过一个东西
那就是我们的函数是否有自己的地址呢,下面让我们来做个测试吧 💖 💖 💖

#include <stdio.h>
void test()
{printf("hehe\n");
}int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}

在这里插入图片描述
从中我们可以发现,函数是有地址的,而且聪明的小爱同学已经偷偷告诉我了

他发现 函数名&函数名 地址居然是一样,并提出了函数名就是函数地址的大胆猜想呢 ! ! !

这个猜想到底对不对呢,我们还有待考证,不妨带着问题我们继续研究吧 😁 😁 😁

<3>. 函数指针的声明

void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;
int Add(int x, int y)
{return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

宝子们都看到了我们函数声明是这样子的,可他有哪些结构呢,是不是还是很模糊呢,下面请看

鱼式疯言

有图有真相
在这里插入图片描述

<4>. 函数指针变量的实际运用

通过函数指针调用指针指向的函数。

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}

在这里插入图片描述

从 pf 和 (*pf) 得出的结果是一致的,这下和小爱同学彻底验证了,我们的函数名就代表地址

鱼式疯言

  1. 函数名相当于数组名一样也表示 地址
  1. int (*) (int ,int)是 函数指针 类型
  1. int (*p)(int ,int ) 是函数指针 变量

二. 函数指针数组

蛙趣,友友们是不是听到这个名字是不是很惊讶呢,那么高级的名字啊,又是函数,又是指针,还是数组的三者结合版啊 !! !

<1>. 函数指针数组的简介

C语言中的函数指针数组是由一组函数指针组成的数组。可以通过函数指针数组来实现函数的动态调用。

函数指针数组的定义方式如下:
// 声明一个函数指针类型
typedef void (*FuncPtr)();
// 声明一个函数指针数组
FuncPtr funcPtrArray[10];

鱼式疯言

函数指针数组本质上是存放地址的 数组,与 函数指针 类似。

<2>. 举个栗子

int add(int x, int y)
{return x + y;
}int sub(int x, int y)
{return x - y;
}int main()
{//声明一个函数指针数组int (*p[2])(int x, int y) = { add,sub };//用 for 循环逐个调用for (int i = 0; i < 2; i++){printf("%d ", p[i](5,4));}return 0;
}

在这里插入图片描述
我们可以利用 函数指针数组 来分别调用不同的函数

鱼式疯言

  1. 函数指针数组本质上是 数组
  1. 竟然是 数组,类型必须一致,包括我们的函数的 参数 以及 返回类型 也是如此

三. 函数指针数组的实际运用:转移表

友友们是否做过简易的 计算器(加减乘除),我想小爱同学一定会这样写

<1>. 一般计算器

//简易计算器普通版
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;
}void meau()
{printf("******* 1.加法 2.减法 *******\n");printf("******* 3.乘法 4.除法 *******\n");printf("*******  0 . 退  出   *******\n");
}
int main()
{int output = 0;do{int a = 0, b = 0;meau();printf("请选择你要进行的运算:");scanf("%d", &output);int t = 0;switch (output){case 1:printf("请输入两个操作数:");scanf("%d%d", &a, &b);t=add(a, b);printf("%d\n", t);break;case 2:printf("请输入两个操作数:");scanf("%d%d", &a, &b);t = sub(a, b);printf("%d\n", t);break;case 3:printf("请输入两个操作数:");scanf("%d%d", &a, &b);t = mul(a, b);printf("%d\n", t);break;	case 4:printf("请输入两个操作数:");scanf("%d%d", &a, &b);t = div(a, b);printf("%d\n", t);break;case 0:printf("计算器正在退出中...\n", t);break;default:printf("选择错误,请重新选择!\n");break;}} while (output);return 0;
}

在这里插入图片描述
这样写固然没错,但我们有没有更好的调用方法呢

<2>. 转移表

#include<stdio.h>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 output = 0;int (*p[5])(int x, int y) = { NULL,add,sub,mul,div };do{int a = 0, b = 0;meau();printf("请选择你要进行的运算:");scanf("%d", &output);int t = 0;if (output > 0 && output < 5){printf("请输入两个操作数:");scanf("%d%d", &a, &b);printf("%d\n", p[output](a, b));}} while (output);return 0;
}

我们由数组转移到函数的调用的这个过程就叫转移表

所以,我们完美的利用了函数指针数组然后利用其下标引用,可以随时调用我们哪一组函数

鱼式疯言

当我们需要调用同类函数时, 函数指针数组 是个不错的选择哦

四. 函数指针的实际运用:回调函数

啥是回调函数呢,友友们先带着这个疑问来探究我们的今天要学的 回调函数 哦 ! ! !

<1>. 回调函数的简介

在C语言中,回调函数 是指一个函数作为参数传递给另一个函数,并且在后者执行过程中被调用的函数。

<2>. 举个栗子

就拿我们上面这个栗子说明吧,我们的普通版本的计算器是不是还可以利用 函数指针 来优化

//回调函数#include<stdio.h>//利用函数指针传参
void calc(int (*pcalc)(int x, int y))
{int m = 0, n = 0;scanf("%d%d", &m, &n);int fault = pcalc(m, n);printf("%d\n", fault);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;
}void meau()
{printf("******* 1.加法 2.减法 *******\n");printf("******* 3.乘法 4.除法 *******\n");printf("*******  0 . 退  出   *******\n");
}int main()
{int intput=0;do{meau();scanf("%d", &intput);switch (intput){case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0 :printf("正在退出中!\n");break;default:printf("选择失败,请重新选择\n");break;}} while (intput);return 0;
}

在这里插入图片描述
小伙伴有没有发现,我们的传递函数时,是用什么来接收的呢,答案应该很明朗了吧,就是我们本篇文章的主角:函数指针

函数指针的出现让我们能够不断重新调用我们重复类型的函数

鱼式疯言

函数 作为 实际参数 时,我们就可以用 函数指针 来作为 形式参数 来接收

函数指针虽好,可不要贪杯哦,他好像我们函数指针数组一样,是要保持 类型统一性

函数指针的优势:可以不用想普通的计算器一样反复调用同样多行语句,从而减少我们代码过多的 冗长

比如
在这里插入图片描述

在这里插入图片描述

总结

  • 函数指针: 理解函数指针的如何声明,为回调函数做好铺垫
  • 函数指针数组:熟悉了函数指针的具体的场景同时也为转移表做好了铺垫
  • 函数指针数组的实际运用:转移表 ——> 真正去贴合实际去理解我们函数指针数组一般的运用场景
  • 函数指针的实际运用:回调函数 ——> 让我们真正理解了当函数作为参数的实际运用

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/680902.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GO语言卵细胞级别教程】05.项目创建和函数讲解

感谢&#xff01;点点赞和评论呀&#xff01;我将继续更新 目录&#xff1a; 感谢&#xff01;点点赞和评论呀&#xff01;我将继续更新0.创建项目1.函数的引入2.注意事项3.详细介绍3.1 形参介绍 4.导入包4.1 基本知识4.2 注意事项 0.创建项目 创建目录 执行命令加载模块 cd 0…

【刷题记录】——时间复杂度

本系列博客为个人刷题思路分享&#xff0c;有需要借鉴即可。 1.目录大纲&#xff1a; 2.题目链接&#xff1a; T1&#xff1a;消失的数字&#xff1a;LINK T2&#xff1a;旋转数组&#xff1a;LINK 3.详解思路&#xff1a; T1&#xff1a; 思路1&#xff1a;先排序&#xf…

【python】print输出的格式化

在Python中&#xff0c;有几种方式来格式化prin函数的输出。以下是一些常见的方法&#xff1a; 1. 使用百分号 % 运算符&#xff08;传统方法&#xff09;: 使用 % 操作符来进行格式化是一种较老的方法&#xff0c;但它仍然有效。 name "Alice"age 25print("…

线性代数的本质 1 向量

向量是线性代数中最为基础的概念。 何为向量&#xff1f; 从物理上看&#xff0c; 向量就是既有大小又有方向的量&#xff0c;只要这两者一定&#xff0c;就可以在空间中随便移动。 从计算机应用的角度看&#xff0c;向量和列表很接近&#xff0c;可以用来描述某对象的几个不同…

【Java程序设计】【C00268】基于Springboot的CSGO赛事管理系统(有论文)

基于Springboot的CSGO赛事管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的赛事管理系统 本系统分为系统功能模块、管理员功能模块、参赛战队功能模块以及合作方功能模块。 系统功能模块&#xff1a;在系…

基于SpringBoot的助农产品采购平台

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

MySQL数据库⑨_事务(四个属性+回滚提交+隔离级别+MVCC)

目录 1. 事务的概念和四个属性 2. 事务的支持版本 3. 事务的提交方式 4. 事务的相关演示 4.1 常规操作_回滚_提交 4.2 原子性_演示 4.3 持久性_演示 4.4 begin自动更改提交方式 4.5 单条SQL与事务的关系 5. 事务的隔离级别 5.1 四种隔离级别 5.2 查看与设置隔离级别…

计算机毕业设计 | vue+SpringBoot选课管理系统(附源码)

1&#xff0c;绪论 1.1 开发背景 随着我国高等教育的发展&#xff0c;数字化校园将成为一种必然的趋势&#xff0c;国内高校迫切需要提高教育工作的质量与效率&#xff0c;学生成绩管理工作是高校信息管理工作的重要组成部分&#xff0c;与国外高校不同&#xff0c;他们一般具…

数字图像处理实验记录八(图像压缩实验)

前言&#xff1a;做这个实验的时候很忙&#xff0c;就都是你抄我我抄你了 一、基础知识 1&#xff0e;为什么要进行图像压缩&#xff1a; 图像的数据量巨大&#xff0c;对计算机的处理速度、存储容量要求高。传输信道带宽、通信链路容量一定&#xff0c;需要减少传输数据量&a…

渗透测试练习题解析 3(CTF web)

1、[网鼎杯 2020 朱雀组]phpweb 1 考点&#xff1a;反序列化漏洞利用 进入靶场&#xff0c;查看检查信息&#xff0c;发现存在两个参数 func 和 p 查看页面源代码 payload&#xff1a;funcfile_get_contents&pphp://filter/resourceindex.php 整理后&#xff0c;就是 PHP 代…

【STM32 CubeMX】HAL库的本质读写寄存器

文章目录 前言一、HAL库的本质1.1 HAL库的本质是操作寄存器1.2 自己实现HAL_GPIO_WritePin寄存器通过寄存器的操作点灯代码概况Port bit set/reset register寄存器 总结 前言 在嵌入式系统开发中&#xff0c;HAL&#xff08;Hardware Abstraction Layer&#xff09;库是一个重…

计算机服务器中了mkp勒索病毒如何解密,mkp勒索病毒解密流程

随着网络技术的不断发展与应用&#xff0c;越来越多的企业走向数字化办公模式&#xff0c;计算机极大地方便了企业的正常生产运营&#xff0c;但网络威胁的手段也不断增加。近期&#xff0c;云天数据恢复接到很多企业的求助&#xff0c;企业的计算机服务器遭到了mkp勒索病毒攻击…

Linux_进程间通信

管道 System V 共享内存 System V IPC 接口介绍 由于进程地址空间的存在&#xff0c;所以进程间有具有独立性&#xff0c;一个进程看不到另一个进程的数据。那么如果我们想让进程间通信&#xff0c;就必须先让它们先看到同一份资源。常见的进程间通信的方法有管道&#xff0c;…

前端学习的笔记第二篇

vscode如何快速生成代码 ! Tab 效果&#xff1a; 解析&#xff1a; <!DOCTYPE html>: 指定当前html版本5。 <html lang"en">: lang > language&#xff0c;en > english。指定当前页面内容是英文的。 <meta charset"UTF-8">:…

猫头虎分享:Win11系统家庭版组策略编辑器怎么打开? Windows11家庭版没有gpedit.msc如何解决?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

滑动小短剧影视微信小程序源码/带支付收益等模式

仿抖音滑动小短剧影视微信小程序源码&#xff0c;带支付收益等模式、支持无限滑动&#xff1b;高性能滑动、预加载、视频预览&#xff0c;支持剧情介绍&#xff0c;集合壁纸另外仿抖音滑动效果&#xff1b;支持会员模式&#xff0c;支持用户单独购买等等多功能。 丰富的后台设…

深度学习基础之《深度学习介绍》

一、深度学习与机器学习的区别 1、特征提取方面 机器学习&#xff1a;人工特征提取 分类算法 深度学习&#xff1a;没有人工特征提取&#xff0c;直接将特征值传进去 &#xff08;1&#xff09;机器学习的特征工程步骤是要靠手工完成的&#xff0c;而且需要大量领域专业知识…

Golang中的fmt包:格式化输入输出的利器

Golang中的fmt包&#xff1a;格式化输入输出的利器 在软件开发的世界里&#xff0c;fmt包就像是一位忠实的伙伴&#xff0c;始终陪伴着开发人员。它简化了格式化输入输出的过程&#xff0c;让打印和扫描数据变得轻松自如。无论是向控制台输出简单的消息&#xff0c;还是处理复杂…

Rust入门:如何在windows + vscode中关闭程序codelldb.exe

在windows中用vscode单步调试rust程序的时候&#xff0c;发现无论是按下stop键&#xff0c;还是运行完程序&#xff0c;调试器codelldb.exe一直霸占着主程序不退出&#xff0c;如果此时对代码进行修改&#xff0c;后续就没法再编译调试了。 目前我也不知道要怎么处理这个事&am…

数据结构——6.3 图的遍历

6.3 图的遍历 一、概念 图的广度优先遍历 树的广度优先遍历&#xff08;层序遍历&#xff09;&#xff1a;不存在“回路”&#xff0c;搜索相邻的结点时&#xff0c;不可能搜到已经访问过的结点&#xff1a; 若树非空&#xff0c;则根节点入队 若队列非空&#xff0c;队头元素…