指针进阶篇(2)

news2025/1/12 20:49:00

进阶指针

 🤔前言🤔

一、😊函数指针😊

二、😜函数指针数组😜

三 、😝指向函数指针数组的指针😝

四、🌝回调函数🌝

🍀小结🍀


🎉博客主页:小智_x0___0x_

🎉欢迎关注:👍点赞🙌收藏✍️留言

🎉系列专栏:C语言进阶篇

🎉代码仓库:Xiaozhi_c

        家银们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!

 

 🤔前言🤔

前面我们学了进阶指针(1),熟悉并了解了字符指针、指针数组、数组指针、数组参数、指针参数,今天我们再来探讨指针。

本章重点:

  1. 函数指针
  2. 函数指针数组
  3. 指向函数指针数组的指针
  4. 回调函数

一、😊函数指针😊

函数指针是指向函数的指针变量。

先看一段代码>

#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();

首先,能存储地址,第一个要求ptun1或者ptfun2是指针,那到底那个是指针呢?

pfun1可以存放,pfun1先和*结合,说明pfun1是指针,指针指向一个函数,指向的是一个无参函数,返回值为void

我们再来看看两个有趣的代码>

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

代码解释>

int main()
{
	//该代码是一次函数调用
	//调用0地址处的一个函数
	//首先代码中将0强制类型转换为类型为void (*)()的函数指针
	//然后去调用0地址处的函数
	//( *( void (*)() ) 0 )();

	typedef unsigned int uint;

	typedef void(*pf_t)(int) ;
	pf_t signal(int, pf_t);

	void (* signal( int, void(*)(int) ) )(int);
	//该代码是一次函数的声明
	//声明的函数名字叫signal
	//signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针能够指向的那个函数的参数是int
	//返回类型是void
	//signal函数的返回类型是一个函数指针,该函数指针能够指向的那个函数的参数是int,返回类型是void
	//void (*)(int) signal(int, void(*)(int));

	return 0;
}

二、😜函数指针数组😜

数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组,

比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)()parr3[10];

答案是:parr1

parr1先和[ ]结合,说明parr1是数组,数组的内容是int(*)()类型的函数指针。

函数指针数组的用途:转移表

我们来看一个简单的例子(计算器):

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;
	do 
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

我们来看运行效果>

我们可以发现switch语句中有许多冗余代码,那该如何去化解这样的问题呢?

我们再来看看使用函数指针数组的实现: 

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 (*pf[5])(int, int) = { NULL, Add, Sub, Mul, Div };

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
			//break;
		}
		else if (input>=1 &&input<=4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pf[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);

	return 0;
}

运行效果>

三 、😝指向函数指针数组的指针😝

指向函数指针数组的指针是一个指针。

指针指向一个数组,数组的元素都是函数指针

void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}

四、🌝回调函数🌝

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

还是熟悉的例子(计算器):

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;
}

void calc(int (*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	
	do 
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		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 (input);

	return 0;
}

我们来画图来详解一下>

🍀小结🍀

今天我们学习了函数指针、函数指针数组、指向函数指针数组的指针、回调函数,相信大家看完有一定的收获。

种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
本节课的代码已上传gitee仓库

 

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

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

相关文章

摩丝-题解

看到题目&#xff0c;怀疑是莫尔斯电码&#xff0c;打开发现果然是莫尔斯电码的点和划.. .-.. --- ...- . -.-- --- ..-简单说一下电报的原理最简单的电报模型就是一个电源&#xff0c;一个开关和一个电磁铁当需要长距离使用时候&#xff0c;需要用到继电器按下开关&#xff0c…

【BP靶场portswigger-服务端10】XML外部实体注入(XXE注入)-9个实验(全)

前言&#xff1a; 介绍&#xff1a; 博主&#xff1a;网络安全领域狂热爱好者&#xff08;承诺在CSDN永久无偿分享文章&#xff09;。 殊荣&#xff1a;CSDN网络安全领域优质创作者&#xff0c;2022年双十一业务安全保卫战-某厂第一名&#xff0c;某厂特邀数字业务安全研究员&…

C#【必备技能篇】使用NPOI实现对excel的读取和写入

文章目录1、Winform界面布局2、引用NPOI的dll3、源码4、运行效果5、NPOI的dll下载地址6、补充【以上步骤只能打开.xls文件&#xff08;97-2003版本&#xff09;&#xff0c;打不开.xlsx文件&#xff08;2007版本&#xff09;】1、Winform界面布局 2、引用NPOI的dll 3、源码 us…

(十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置

前言 本节内容我们主要介绍一下在jenkins中如何集成sonar qube代码质量检查工具&#xff0c;sonar qube可以在流水化项目集成部署前对我们的代码质量检查。开始本节内容前我们需要先搭建好sonar qube服务&#xff0c;关于sonar qube服务的搭建可参考作者往期博客内容&#xff…

P4391 [BOI2009]Radio Transmission 无线传输

题目描述 给你一个字符串 s_1s1​&#xff0c;它是由某个字符串 s_2s2​ 不断自我连接形成的。但是字符串 s_2s2​ 是不确定的&#xff0c;现在只想知道它的最短长度是多少。 输入格式 第一行一个整数 LL&#xff0c;表示给出字符串的长度。 第二行给出字符串 s_1s1​ 的一个子…

【linux入门】基础知识学习笔记

文章目录【第一章-宏观知识】1.硬件和软件的关系2.操作系统 是什么、作用是什么3.常见的操作系统4.Linux的诞生5.Linux内核 是什么6.Linux发行版 是什么7.WSL是什么8.虚拟机快照9.FinalShell&#xff08;Xshell替代品&#xff09;【第二章-Linux基础命令】1.Linux目录结构2.什么…

Linux---权限

目录 1.文件访问者的分类&#xff08;人/用户&#xff09; 2.文件类型和访问权限&#xff08;事物属性&#xff09; 3.文件权限值的表示方法 a)字符表示方法 b)8进制数值表示方法 4.文件访问权限的相关设置方法 4.1 改属性 4.2 改人&#xff08;改拥有者/所属组&#xff09;…

数组常用方法总结 (3) :map / forEach / every / some

map 遍历数组的每一项。如果是简单数组&#xff0c;不改变原始数组&#xff08;值类型&#xff09;。如果是对象数组&#xff0c;原始数组可以被改变&#xff08;引用类型&#xff09;。遍历原始数组&#xff0c;返回值为原始数组的每一项&#xff0c;最终可组合成新数组。 简…

LeetCode 78 子集 | 解题思路分享

原题链接&#xff1a;78. 子集 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;中等 题目描述 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任…

忆享聚焦|5G投资、网络安全市场、云计算、Web3技术……近期热点资讯一览

“忆享聚焦”栏目第11期来啦&#xff01;本栏目汇集近期互联网最新资讯&#xff0c;聚焦前沿科技&#xff0c;关注行业发展动态&#xff0c;筛选高质量讯息&#xff0c;拓宽用户视野&#xff0c;让您以最低的时间成本获取最有价值的行业资讯。目录行业资讯1. SA&#xff1a;全球…

ubuntu虚拟机VmWare与主机共享文件夹

一、说明&#xff1a; 宿主操作系统&#xff1a;Windows 11 64位。 客户操作系统&#xff1a;Ubuntu 18.04.1 64位。 虚拟机软件&#xff1a;VMware Workstation 17 pro 二、步骤&#xff1a; 1、参考教程链接1&#xff0c;在主机设置共享文件夹。 注意&#xff1a;教程链接…

聚焦技术,2022巨杉荣获国内外多家权威机构认可

作为分布式数据库的领先企业 巨杉成立十年来&#xff0c;一直聚焦分布式技术的自研与深耕 在分布式数据库领域已取得丰硕的成果 回望2022&#xff0c;巨杉除在客户案例及产品方面屡获殊荣外 也凭借过硬的技术实力及规模化的行业应用 得到多家国内外权威机构的认可 国际权威…

TensorRT学习笔记--基本概念和推理流程

目录 前言 1--Tensor RT基本概念 2--推理流程 3--实例代码 前言 以下 Tensor RT 的基本概念和推理流程均为博主自我的理解&#xff0c;可能部分内存会存在错误或偏差&#xff0c;仅供参考&#xff01; 1--Tensor RT基本概念 ① Logger&#xff1a;日志记录器&#xff0c;…

ssm:spring定时任务Task和CronExpression表达式

开发一个定时任务&#xff1a;每天晚上23点执行数据归集任务 首先Spring配置文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.or…

Java中的常用的代理模式

本文介绍在Java种常用的3种动态代理。 代理模式是23种模式中的一种&#xff0c;属于结构型设计模式。这种模式的作用就是要创建一个中间对象&#xff08;相当于中介或者代理对象&#xff09;&#xff0c;通过操作中间对象来间接调用目的对象的方法&#xff0c;字段等&#xff0…

Everything搜索知识总结

1.只知道那个文件以 .txt结尾 .*\.txt$ ($表示以什么结尾) 2.搜索某个路径下的文件 D:\ configure.bat (搜索D盘下的该文件,注意要用这种类型的"\",和被搜索的文件之间有空格;要先打出路径,再打出搜索文件.) 3.搜索指定路径下的多个文件 路径\ 文件1 | …

Halcon亚像素边缘缺陷检测案例

一、下面的案例是总结的Halcon边缘缺陷检测的一种情况。本案例是利用阈值分割获取金属区域&#xff0c;并利用boundary和edges_sub_pix获取到亚像素边缘。然后综合利用fit_rectangle2_contour_xld拟合出金属对应的放射矩形&#xff0c;最后利用dist_rectangle2_contour_points_…

【小白课程】openKylin用户手册原理解析,一招教你学会自定义!

openKylin用户手册是详细描述openKylin操作系统的功能和用户界面&#xff0c;让用户了解如何使用该软件的说明书。通过阅读openKylin用户手册&#xff0c;能够更快更好的上手和使用openKylin操作系统。今天就带大家简单了解下openKylin用户手册的实现原理以及如何自定义用户手册…

用EditPlus编译Fortran

一、EditPlus配置 语法点亮 安装好EditPlus后&#xff0c;点击Tool->Prefenrences&#xff0c;在File->Setting&syntex下&#xff0c;点击Add按钮&#xff0c;填Frotran。 到EditPlus官网上 EditPlus - User Files (other files) 下载Fortran语法文件 ​ 二、配置…

设计模式学习(四):Strategy策略模式

一、什么是Strategy模式 Strategy的意思是“策略”&#xff0c;指的是与敌军对垒时行军作战的方法。在编程中&#xff0c;我们可以将它理解为“算法”。无论什么程序&#xff0c;其目的都是解决问题。而为了解决问题&#xff0c;我们又需要编写特定的算法。使用Strategy模式可以…