那些 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.mfbz.cn/a/385555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BUUCTF LKWA

1.访问页面。 2.选择 Variables variable 关卡 3.获得flag http://357dab81-78b8-4d74-976a-4a69dd894542.node5.buuoj.cn:81/variables/variable.php?funcpassthru&inputcat%2Fflagflag{0020ced6-8166-4fa5-87a7-7d93ee687c3e}

键盘重映射禁用 CtrlAltDel 键的利弊

目录 前言 一、Scancode Map 的规范 二、禁用 CtrlAltDel 的方法及其缺陷 三、编程实现和测试 3.1 C 实现的简易修改工具 3.2 C# 实现的窗口工具 四、总结 本文属于原创文章&#xff0c;转载请注明出处&#xff1a; https://blog.csdn.net/qq_59075481/article/details…

PySQLRecon:一款功能强大的MSSQL安全测试工具

关于PySQLRecon PySQLRecon是一款功能强大的MSSQL安全测试工具&#xff0c;该工具基于SQLRecon实现其功能&#xff0c;可以帮助广大红队研究人员针对MSSQL执行攻击性安全测试。 环境配置 由于该工具基于Python 3开发&#xff0c;因此我们首先需要在本地设备上安装并配置好Pyt…

鸿蒙小案例-你画我猜

鸿蒙小案例-你画我猜 1.准备组件(组件布局) 2.实现跟随鼠标画笔画出图案功能 3.实现复制上面的画笔的图案功能 4.其他小功能1.组件的准备 画布的组件官方给的API是Canvas&#xff0c;需要传递一个参数CanvasRenderingContext2D 直接搜索API 使用官方案例 private settings: …

小技巧 | 如何将win11回退至win10

当你不小心点错将电脑的win10系统更新成win11系统了&#xff08;但没有完全更新&#xff09;&#xff0c;你会怎么做呢&#xff1f; 1.在“开始”菜单界面点击“设置”选项 2.点击“更新和安全”选项 3.在“更新历史记录”中点击“恢复选项” 4.点击“返回”选项 会弹出以下界面…

three.js 细一万倍教程 从入门到精通(二)

目录 三、全面认识three.js物体 3.1、掌握几何体顶点_UV_法向属性 3.2、BufferGeometry设置顶点创建矩形 3.3、生成酷炫三角形科技物体 四、详解材质与纹理 4.1、初识材质与纹理 4.2、详解纹理偏移_旋转_重复 偏移 旋转 重复 4.3、设置纹理显示算法与mipmap mapFil…

【动态规划】:泰波那契模型_解码方法

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本专栏是关于各种算法的解析&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数据结构专栏&…

【网工】华为设备命令学习(综合实验一)

实验要求和实验成果如图所示。 LSW2不需要其他配置&#xff0c;其下就一台设备&#xff0c;不需要区分。 LSW3配置如下&#xff1a; <Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]un in en //关闭系统提示信息 Info: Information …

LeetCode、72. 编辑距离【中等,二维DP】

文章目录 前言LeetCode、72. 编辑距离【中等&#xff0c;二维DP】题目链接与分类二维DP 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容…

VTK Python PyQt 监听键盘 控制 Actor 移动 变色

KeyPressInteractorStyle 在vtk 中有时我们需要监听 键盘或鼠标做一些事&#xff1b; 1. 创建 Actor&#xff1b; Sphere vtk.vtkSphereSource() Sphere.SetRadius(10)mapper vtk.vtkPolyDataMapper() mapper.SetInputConnection(Sphere.GetOutputPort()) actor vtk.vtkAc…

Vue核心基础6:Vue内置指令、自定义指令、生命周期

1 Vue中的内置指令 <script>const vm new Vue({el: #root,data: {n: 1,m: 100,name: Vue,str: <h3>你好</h3>}})</script> 1.1 v-text <div v-text"name"></div>1.2 v-html <div v-html"str"></div> …

Spring Boot 笔记 012 创建接口_添加文章分类

1.1.1 实体类添加校验 package com.geji.pojo;import jakarta.validation.constraints.NotEmpty; import lombok.Data;import java.time.LocalDateTime;Data public class Category {private Integer id;//主键IDNotEmptyprivate String categoryName;//分类名称NotEmptypriva…

中小学信息学奥赛CSP-J认证 CCF非专业级别软件能力认证-入门组初赛模拟题第一套(完善程序题)

CCF认证CSP-J入门组模拟测试题第一套 三、完善程序题 第一题 九宫格 请完善下面的程序,将1~9个数字分别填人3x3的九宫格中,第一行的三个数字组成一个三位数。要使第二行的三位数是第一行的2倍,第三行的三位数是第一行的3倍且每个格子里的数字都不能重复,现在要求输出所有的填…

python输出字符 2022年12月青少年电子学会等级考试 中小学生python编程等级考试二级真题答案解析

目录 python输出字符 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python输出字符 2022年12月 python编程等级考试级编程题 一、题目要求 …

《CSS 简易速速上手小册》第2章:CSS 布局与定位(2024 最新版)

文章目录 2.1 Flexbox&#xff1a;灵活的布局解决方案2.1.1 基础知识2.1.2 重点案例&#xff1a;创建一个响应式导航菜单2.1.3 拓展案例 1&#xff1a;卡片布局2.1.4 拓展案例 2&#xff1a;中心对齐的登录表单 2.2 Grid 布局&#xff1a;网格系统的魔力2.2.1 基础知识2.2.2 重…

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

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

数据结构与算法:单链表

朋友们大家好&#xff0c;本节来到数据结构与算法的新内容&#xff1a;单链表 在上篇文章中&#xff0c;我们知道顺序表通常需要预分配一个固定大小的内存空间&#xff0c; 通常以二倍的大小进行增容&#xff0c;可能会造成空间的浪费&#xff0c;本篇文章我们介绍的链表可以解…

去除vue自带的边距

使用vue时发现总有去不掉的外边距&#xff0c;在index.vue里面怎样设置样式都不管用 查阅资料后发现要在vue项目自带的index.html文件内添加下面的样式代码才行 <style>*{margin: 0;padding: 0;}body,html{margin: 0;padding: 0;} </style>

二、DataX安装

DataX安装 一、简介二、系统要求三、部署 一、简介 官方地址&#xff1a;https://github.com/alibaba/DataX/blob/master/userGuid.md 二、系统要求 LinuxJDK(1.8以上&#xff0c;推荐1.8) Centos7.9的java1.8安装命令&#xff1a;yum install java-1.8.0-openjdk.x86_64 Py…

【新书推荐】7.6节 相对基址加变址寻址方式

本节内容&#xff1a;基址加变址寻址方式。 ■基址加变址寻址方式&#xff1a;指令操作数为内操作数&#xff0c;操作数地址使用[基址寄存器变址寄存器]表示。 7.6.1 基址加变址寻址方式 基址加变址寻址方式的操作数在存储器中&#xff0c;操作数的有效地址由基地址寄存器&am…
最新文章