指针---进阶篇(二)

news2024/12/25 13:55:33

指针---进阶篇(二)

  • 前言
  • 一、函数指针
    • 1.抛砖引玉
    • 2.如何判断函数指针?(方法总结)
  • 二、函数指针数组
    • 1.什么是函数指针数组?
    • 2.讲解函数指针数组
    • 3.模拟计算器:讲解函数指针数组
  • 三、指向函数指针数组的指针
  • 四、回调函数
  • 五、qsort排序

前言

那么好了好了,宝子们,从今天开始开始总结暑假博客,从指针开始,后续,来吧开始整活!⛳️

一、函数指针

1.抛砖引玉

直接上代码:

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

在这里插入图片描述
我打印的是函数的地址。但是如果我们想要把函数的地址保存起来,我们该怎么样操作呢?

void test()
{
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

在这里我们需要明白一点,当时我们学数组的时候,我们知道数组名就是数组首元素的地址,在这里函数也是一样的,函数名也可以代表函数的首元素地址!
首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?前面的进阶一里面我们讲过了:数组指针。我们呢可以类比一下
答案是:pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

2.如何判断函数指针?(方法总结)

比如说下面这段代码:


int add(int x, int y)//加法
{
	return x + y;
}

int main()
{
	int (*pf)(int, int) = add;
	
	int m = add(3, 4);
	int n = pf(4, 5);
	printf("%d %d\n", m, n);
	return 0;
}

在这里我来教一下大家怎样判别,什么是函数指针,指针函数,数组指针,指针数组之类的。
就以这个为例:int (*pf)(int,int)=add;
在这个语句里面变量是pf,pf被一个小括号扩住,pf先与 *结合,所以说它以指针结尾。首先分析完了他是一个指针,然后他是什么类型的指针呢?后面是参数,两个参数类型都是int,前面是返回类型也是int,所以说它是一个标标准准的函数指针。
(在这里一个规律就是:变量和XX先结合就以XX为结尾)

二、函数指针数组

1.什么是函数指针数组?

首先我们要明白什么是数组?
数组的概念是:数组是一个储存相同元素的集合。
不要害怕他前面这么复杂。又是函数,又是指针,又是数组,我们该如何判断呢?还是运用我上面的规律总结。

2.讲解函数指针数组

好的,我们现在直接上栗子:


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 (*jia)(int, int) = add;
	int (*jian)(int, int) = sub;
	int (*cheng)(int, int) = mul;
	int (*chu)(int, int) = div;

	int (*pfarr[4])(int, int) = { add,sub,mul,div };
	//上面的数字里面储存的都是各个函数名,所以就是储存的函数的地址
	return 0;
}

我们来分析一下这个 函数指针数组:
int (*pfarr[4])(int, int) = { add,sub,mul,div };
看这样复杂的语句的时候,我们先看变量名,变量名是pfarr,先看变量名与谁先结合,由于这里的方括号[ ]的结合度比 *高,所以pfarr先与方括号[ ]结合,所以说它就是以数组来结尾的。

只要你有几个函数,并且函数的返回类型都是一模一样的,你就可以把这几个函数的地址放在一个数组里面,那么这个数组就叫做函数指针数组!
如何写一个函数指针数字呢?那当然是从函数指针来写起,然后再加一个数组

3.模拟计算器:讲解函数指针数组

1.常规的使用普通函数来实现


#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}


//常规的使用普通函数来实现
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

2.函数指针数组实现


#include <stdio.h>
int add(int a, int b)
{
	return a + b;

}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

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

何为指向函数指针数组的指针?简单的来讲就是函数指针数组的地址
指向函数指针数组的指针是一个 指针 指针指向一个 数组 ,数组的元素都是 函数指针 ;

上代码:

#define _CRT_SECURE_NO_WARNINGS 1 
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)[10])(const char*) = &pfunArr;
	return 0;
}

四、回调函数

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

五、qsort排序

接下来我通过用qsort排序来展示一下回调函数的魅力:

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

使用回调函数,模拟实现qsort(采用冒泡的方式)。
注意:这里第一次使用 void* 的指针,讲解 void* 的作用。


#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}
int main()
{
	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	//char *arr[] = {"aaaa","dddd","cccc","bbbb"};
	int i = 0;
	bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

好了,今天的分享就到这里了

如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!

在这里插入图片描述

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

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

相关文章

8年测试整理,接口测试-测试点分析与搜索查询测试点(超细汇总)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口可用性 主要…

Redux - Redux在React函数式组件中的基本使用

文章目录 一&#xff0c;简介二&#xff0c;安装三&#xff0c;三大核心概念Store、Action、Reducer3.1 Store3.2 Reducer3.3 Action 四&#xff0c;开始函数式组件中使用4.1&#xff0c;引入store4.1&#xff0c;store.getState()方法4.3&#xff0c;store.dispatch()方法4.4&…

10年软件测试行业经验教你如何写简历【绝对靠谱】

前言 作为软件测试的从业者&#xff0c;面试或者被面试都是常有的事。 可是不管怎样&#xff0c;和简历有着理不清的关系&#xff0c;面试官要通过简历了解面试者的基本信息、过往经历等。 面试者希望通过简历把自己最好的一面体现给面试官&#xff0c;所以在这场博弈中&…

若依vue -【 100 ~ 更 ~ 110 】

100 主子表代码生成详解 1 新建数据库表结构&#xff08;主子表&#xff09; -- ---------------------------- -- 客户表 -- ---------------------------- drop table if exists sys_customer; create table sys_customer (customer_id bigint(20) not null…

母猪产仔早知道,这次南农用上了英伟达边缘 AI Jetson

内容一览&#xff1a;对养猪业而言&#xff0c;母猪产仔是其中关键的一环。因此&#xff0c;提高猪仔成活率、确保母猪分娩过程安全&#xff0c;成为重要课题。现有的 AI 监测方式&#xff0c;存在着高设备成本与信息传输不稳定的问题&#xff0c;南京农业大学研究人员&#xf…

Kali Hyper-V安装正常启动后 黑屏 只能进命令模式

问题: Hyper-V安装虚拟机Kali系统一切安装正常, 没有出现错误. 安装成功后重启,只能进入命令模式,tt1-tt6,进不去GUI桌面. 尝试: 一代二代虚拟硬盘都试过,同样问题,只能开进后进入命令模式,在命令模式下一切运行正常, 也修复过系统 GNOM等的,不管用. 以下为国外论坛给的建议,尝…

Qt 文件对话框使用 Deepin风格

当你在Deepin或UOS 上开发 Qt 程序时&#xff0c;如果涉及到文件对话框功能&#xff0c;那么就会遇到调用原生窗口的问题。 如果你使用的是官方的Qt版本&#xff0c;那么在Deepin或者UOS系统上&#xff0c;弹出的文件对话框会是如下这样&#xff1a; 而Deepin或UOS系统提供的默…

【AI理论学习】手把手推导扩散模型:Diffusion Models(DDPM)

手把手推导扩散模型&#xff1a;Diffusion Models&#xff08;DDPM&#xff09; DDPM理论回顾前置知识过程详解Forward ProcessReverse Process DDPM算法伪代码训练部分采样部分 总结一下 参考链接 在这篇博客文章中&#xff0c;我们将深入研究 去噪扩散概率模型(也称为 DDPM&…

利用Simulink Test进行模型单元测试 - 1

1.搭建用于测试的简单模型 随手搭建了一个demo模型MilTestModel&#xff0c;模型中不带参数 2.创建测试框架 1.模型空白处右击 测试框架 > 为‘MilTestModel’创建 菜单 2.在创建测试框架对话框中&#xff0c;点击OK&#xff0c;对应的测试框架MilTestMode_Harness1就自动…

js玩儿爬虫

前言 提到爬虫可能大多都会想到python&#xff0c;其实爬虫的实现并不限制任何语言。 下面我们就使用js来实现&#xff0c;后端为express&#xff0c;前端为vue3。 实现功能 话不多说&#xff0c;先看结果&#xff1a; 这是项目链接&#xff1a;https://gitee.com/xi1213/w…

时间复杂度与空间复杂度的详解

目录 1.时间复杂度 2.时间复杂度计算例题 3.空间复杂度 1.时间复杂度 算法中的基本操作的执行次数&#xff0c;为算法的时间复杂度。 如何表达 时间复杂度&#xff1f; 大O的渐进表示法 实际中我们计算时间复杂度时&#xff0c;我们其实并不一定要计算精确的执行次数&#xf…

105. 从前序与中序遍历序列构造二叉树

题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,n…

【高频面试题】多线程篇

文章目录 一、线程的基础知识1.线程与进程的区别2.并行和并发有什么区别&#xff1f;3.创建线程的方式有哪些&#xff1f;3.1.Runnable 和 Callable 有什么区别&#xff1f;3.2.run()和 start()有什么区别&#xff1f; 4.线程包括哪些状态&#xff0c;状态之间是如何变化的4.1.…

一文详述流媒体传输网络MediaUni

一张「多元融合」的网络。 黄海宇&#xff5c;演讲者 大家好&#xff0c;我是阿里云视频云的黄海宇&#xff0c;今天分享主题是MediaUni——面向未来的流媒体传输网络设计与实践。 下面我将会从应用对流媒体传输网络的要求、MediaUni定位与系统架构、MediaUni技术剖析、基于Me…

vr虚拟仿真消防模拟演练提升受训者的安全观念和防范技能

纵观多年来的火灾事故教训得知&#xff0c;火灾发生的原因复杂多样&#xff0c;仅采取单一教育形式无法达到预期效果。消防安全重在预防&#xff0c;VR消防模拟演练系统将火灾安全问题&#xff0c;经采集和汇集处理&#xff0c;以可视化的形式在安全培训平台上进行实时展现&…

微服务与Nacos概述-3

流量治理 在微服务架构中将业务拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff0c;但是由于网络原因或者自身的原因&#xff0c;服务并不能保证服务的100%可用&#xff0c;如果单个服务出现问题&#xff0c;调用这个服务就会出现网络延迟&#xff0c;此时…

基于STM32 FOC下桥三电阻采样方式的电机相电流重构方法

文章目录 1、本文中的PWM生成模式2、 注意事项3、与SVPWM相关的问题4、采样点的选择4.1、在低调制系数时&#xff08;1&#xff09;4.2、在高调制系数时&#xff08;2&#xff09;4.3、在高调制系数时&#xff08;3&#xff09;4.4、在高调制系数时&#xff08;4&#xff09; 5…

Oracle 使用 CONNECT_BY_ROOT 解锁层次结构洞察:在 SQL 中导航数据关系

CONNECT_BY_ROOT 是一个在 Oracle 数据库中使用的特殊函数&#xff0c;它通常用于在层次查询中获取根节点的值。在使用 CONNECT BY 子句进行层次查询时&#xff0c;通过 CONNECT_BY_ROOT 函数&#xff0c;你可以在每一行中获取根节点的值&#xff0c;而不仅仅是当前行的值。 假…

Window下安装MinGW64

欢迎来到我的酒馆 介绍Windows下&#xff0c;安装MinGW64。 目录 欢迎来到我的酒馆二.MinGW64三.配置系统环境变量 二.MinGW64 从sourceforge下载mingw64&#xff0c; sourceforge下载MinGW https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/ 下…

效率指数级提升的Intellij IDEA快捷键集合

温馨提示&#xff1a;全文有18个小技巧&#xff0c;为了阅读体验&#xff0c;可以直接先看文章目录。 1&#xff0c;打开一个文件中的所有方法展示框 CtrlF12 Alt7 2&#xff0c;打开一个类的所有使用位置 AltF7 3&#xff0c;打开一个类在项目使用的位置 CtrlAltF7 4&#…