指针进阶(3)--玩转指针

news2024/11/26 18:29:26

指针进阶

内容不多,但面面俱到,都是精华

在这里插入图片描述

1.回调函数:

2.详解qsort函数参数:

回调函数就是,把一个函数的地址,放在函数指针中,然后将该指针作为一个参数,传到
另一个函数中,在这个函数内部使用了外部写好的一个函数.
举一个例子,看完你一定明白了

例子:


void menu(void)
{
	printf("*************************\n");
	printf("**1.Add   2.Sub**********\n");
	printf("**3.Mul   4.Div**********\n");
	printf("********0.exit***********\n");
}

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

void cal(int (*pf)(int a, int b))//函数指针
{			//这里面存放的是你需要选用的函数的地址
	int a = 0;
	int b = 0;
	printf("请输入两个数:");
	scanf("%d%d", &a, &b);
	printf("结果为%d\n", pf(a, b));
}

int main()
{
	int input = 0;

	do
	{
		menu();

		printf("请选择:->\n");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			cal(Add);
			//在一个函数内部调用另一个已经写好的函数
			break;
		case 2:
			cal(Sub);
			break;
		case 3:
			cal(Mul);
			break;
		case 4:
			cal(Div);
			break;
		case 0:
			printf("退出\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} 
	while (input);
}

简单的说,就是在一个函数内部调用另一个已经写好的函数

在这里插入图片描述
图解如上:

理解回调函数后,下面引出一个函数,叫做 qsort 函数

函数参数如下

void qsort (void* base, size_t num, size_t size,
            int (*compar)(const void*,const void*));
参数1 待排序数组首元素地址
参数2 元素个数
参数3 元素大小--单位是字节
参数4 函数指针,比较两个元素的方法和函数的地址,该函数自己实现

详解qsort函数参数:

1.为什么指针是void类型呢?
void指针,可以理解成一个万能桶,什么类型的地址都能放到void的指针里面
举一个简单的例子:

int a =10 ;
void*p = &a;
char ch ='w' ; 
p = & ch ;

在这里插入图片描述
编译该段代码可以发现,未发生错误

注意看,调试过程中,指针p存放的地址由a的地址变成了ch的地址,证明什么类型的地址都能放到void*的指针里面

在这里插入图片描述

由于void是无类型指针,所以void类型指针不能进行加减操作,不能进行解引用操作,想要进行这些操作,需要把void类型强制类型转成所需要的类型才能操作

在这里插入图片描述

编译无法通过;

2.函数指针,比较两个元素的方法和函数的地址,该函数自己实现什么意思呢?

直接上代码:
下面排序一个 int 类型数组的元素

int cmp_int(const void* e1, const void* e2)
{
	return  *(int*)e1 - *(int*)e2; 
}
该函数实现的是 两个元素 比较的方法

int main()
{
	int arr[] = { 2,3,5,7,1,4,9,6,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	参数1:数组首元素地址,参数2:元素个数
	参数3:每个元素大小(单位字节)
	参数4:函数指针
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

由于 void指针无类型,故在比较函数内部,需要把void指针强制类型转化为所需要的类型

可以想象 qsort 函数是拿到比较的方法后,通过这个方法比较完其他的所有元素

代码运行结果如下:

在这里插入图片描述
非常方便快捷得出结果

再来一个例子,假如要排序结构体呢?

int cmp_struct(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
	//以名字来对结构体进行排序
}

int main()
{
	struct stu student[3] = { {"张三",20},{"李四",19},{"王五",18}};
	int sz = sizeof(student) / sizeof(student[0]);
	qsort(student, sz, sizeof(student[0]), cmp_struct);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d\n", student[i].name, student[i].age);
	}
}

对结构体进行排序时,根据需求,是按名字字母顺序排序还是按年龄排序
对于结构体排序来说 , e1需要强制类型转化为结构体类型指针
在这里插入图片描述
按名字排序的结果如上:

总结:
qsort函数参数的理解重点在于函数指针
就是在外部写好一个函数,将该函数的地址作为参数传给qsort函数,
外部写好的函数就是 回调函数

3。自己编写冒泡排序函数,要求能对任意类型的数据进行排序
(仿照qsort参数)

代码剖析:
要自己实现冒泡排序,基本的框架和思想是不变的,两层循环进行排序

下面开始深入剖析每段代码的含义:

1.主函数实现

int main()
{
	char arr[][15] = { "hello","world","i love china" };

	int sz = sizeof(arr) / sizeof(arr[0]);
	
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_char);
	for (int i = 0; i < sz; i++)
	{
		printf("%s\n",arr[i]);
	}
}

2.冒泡排序函数实现,具体请看下面

void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{					//base是数组首元素地址      函数指针,存放的是cmp比较函数的地址
	int i = 0;
	int j = 0;
	for (i = 0; i < sz -1; i++)
	{
		for (j = 0; j < sz - i -1; j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
			{
				//把void*指针强制类型转化成char*指针,因为不知道未来要排序的元素是什么类型
					// 那就统一变成char*,是一个字节,加上宽度,就能知道每个元素是几个字节了
				//比较函数   base+每个元素的大小(宽度width) 意味着移动到该元素下
			   //j =0时,比较第一个和第二个元素,j=1时,比较第二个和第三个元素
				swap( (char*)base + width * j, (char*)base + width * (j + 1), width);
					
					//交换  这里不仅要传两个要交换的元素的地址过去,还要知道这两个元素有多长,就需要传width
					//否则无法交换完成
			}
		}
	}
}

3.交换函数实现

void swap(char* buf1, char* buf2 ,int width)
{    //这里用char*的指针来接受,每移动一次就是1个字节
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
		//每执行交换一次,指针往后移继续交换,直到把两个数组里面的元素都交换为止
	}
}

width 已经在主函数计算过,现在直接使用即可

4.cmp_char函数实现

int cmp_char(const void* e1, const void* e2)
{
	return strcmp( (char*)e1 , (char*)e2 );
}

在这里插入图片描述
结果如上:

假如不想比较字符串了,想对结构体进行排序:
1.重新写一个函数,这个函数实现的是比较结构体的方法:
如下:

struct stu
{
	char name[20];
	int age;
};

先初始化结构体

int cmp_struct_by_age(const void* e1, const void* e2)
{
	//return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
	//以名字来对结构体进行排序
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age; 
	//以年龄对结构体进行排序
}

自定义比较函数,通过年龄来比较,先把e1和e2强转为结构体类型指针,通过结构体指针直接访问年龄。

int main()
{
	struct stu student[3] = { {"张三",19},{"李四",20},{"王五",18}};
	int sz = sizeof(student) / sizeof(student[0]);
	bubble_sort(student, sz, sizeof(student[0]), cmp_struct_by_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d\n", student[i].name, student[i].age);
	}
}

设置好函数比较方法后,直接调用即可

下面是运行结果

在这里插入图片描述

说到这里,把全部代码贴出来,理解之后,你可以运行一下看看效果

struct stu
{
	char name[20];
	int age;
};

void swap(char* buf1, char* buf2, int width)
{    //这里用char*的指针来接受,每移动一次就是1个字节
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int sz, int width, int (*cmp)(void* e1, void* e2))
{					//base是数组首元素地址
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char*)base + width * j, (char*)base + width * (j + 1)) > 0)
			{
				//把void*指针强制类型转化成char*指针,目的是不知道未来要排序的元素是什么类型
					// 那就统一变成char*,是一个字节,加上宽度,就能知道每个元素是几个字节了
				//比较函数   base+每个元素的大小(宽度width) 意味着移动到该元素下
			   //j =0时,比较第一个和第二个元素,j=1时,比较第二个和第三个元素
				swap((char*)base + width * j, (char*)base + width * (j + 1), width);

				//交换  这里不仅要传两个要交换的元素的地址过去,还要知道这两个元素有多长,就需要传width
				//否则无法交换完成
			}
		}
	}
}

int cmp_struct_by_age(const void* e1, const void* e2)
{
	//return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
	//以名字来对结构体进行排序
	return ((struct stu*)e1)->age - ((struct stu*)e2)->age; 
	//以年龄对结构体进行排序
}

int main()
{
	struct stu student[3] = { {"张三",19},{"李四",20},{"王五",18}};
	int sz = sizeof(student) / sizeof(student[0]);
	bubble_sort(student, sz, sizeof(student[0]), cmp_struct_by_age);
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d\n", student[i].name, student[i].age);
	}
}

今天就到这里,我们下次见
在这里插入图片描述

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

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

相关文章

04.南瓜树低代码平台平台 分析后的感想

随着企业产品的不断完善&#xff0c;后续将有时间来推进产品转向低代码平台化。 低代码平台不是无代码平台&#xff0c;采用配置的方式完成UI/流程/报表的处理&#xff0c;有业务人员在完成基本的产品框架后&#xff0c;由研发人员完成业务规则代码固化&#xff0c;最终达到产…

MobSDK 封装MobSDK基础包

平台兼容性 Android Android CPU类型 iOS 适用版本区间&#xff1a;4.4 - 12.0 armeabi-v7a&#xff1a;支持&#xff0c;arm64-v8a&#xff1a;支持&#xff0c;x86&#xff1a;支持 原生插件通用使用流程&#xff1a; 购买插件&#xff0c;选择该插件绑定的项目。在HB…

3. 实例化Bean的三种方式

实例化Bean的三种方式 一、构造方法方式 1.1 BookDaoImpl package com.lin.dao.daoimpl;import com.lin.dao.BookDao;public class BookDaoImpl implements BookDao {public BookDaoImpl() {System.out.println("BookDao的无参构造器");}/*** 数据层实现*/public …

了解Linux 操作系统!开篇!!!

【推荐阅读】 Linux内核CPU调度域内容讲解 关于如何快速学好&#xff0c;学懂Linux内核。内含学习路线 一文了解Linux上TCP的几个内核参数调优 Linux 接口 Linux 系统是一种金字塔模型的系统&#xff0c;如下所示 应用程序发起系统调用把参数放在寄存器中(有时候放在栈中)…

Python解题 - CSDN周赛第15期 - 客栈的咖啡

本期遇上官方大放水&#xff0c;四道题里有三道都在每日一练里做过&#xff0c;再加上比赛时间不太友好&#xff0c;参与人数不多&#xff0c;问哥竟然混了个第一名&#xff0c;真是惭愧。。。就当是官方在奖励那些平时多多参加每日一练的童鞋们了。 第一题&#xff1a;求并集 …

Vue3响应式原理设计和实现

Vue3响应式原理设计和实现响应式什么是响应式手动响应式proxy代理对象响应式系统一个属性注册一个副作用函数一个属性注册多个副作用函数多个属性注册不同的副作用函数多个数据不同属性注册不同的副作用函数响应式 什么是响应式 响应式是一个过程&#xff0c;这个过程存在两个…

【MaixPy】:K210识别简例(简单二维码检测和双二维码检测)

实物图 俩二维码识别实物图 前言 这段时间接触了一下基于MaixPy的开发K210的摄像头设备,的确很有趣,运行速度很快,编程难度不大。很适合咱们视觉开发的同学们学习,以下是我玩设备的一些感悟,如有不妥之处,希望大家雅正,也希望能帮助初学者了解和学习,也可加bulidupup(…

java毕业设计——基于java+Socket+sqlserver的网络通讯系统设计与实现(毕业论文+程序源码)——网络通讯系统

基于javaSocketsqlserver的网络通讯系统设计与实现&#xff08;毕业论文程序源码&#xff09; 大家好&#xff0c;今天给大家介绍基于javaSocketsqlserver的网络通讯系统设计与实现&#xff0c;文章末尾附有本毕业设计的论文和源码下载地址哦。 文章目录&#xff1a; 基于jav…

LwIP源码分析(3):内存堆和内存池代码详解

文章目录1 内存堆1.1 mem_init1.2 mem_malloc1.3 mem_free2 内存池2.1 memp_init2.2 memp_malloc2.3 memp_free3 内存管理宏定义在嵌入式系统中&#xff0c;内存池有助于快速有效地分配内存。LwIP提供了两个灵活的方式来管理和组织内存池的大小&#xff1a;内存堆和内存池。当然…

电脑维修记录

记于2022年12月15日 今天把电脑修好了&#xff0c;总结这次经验&#xff1a; &#xff08;1&#xff09;无知者无畏&#xff0c;对任何事情都要抱有探索的精神&#xff0c;最遗憾的事情不是做错了&#xff0c;而是想做没去做 &#xff08;2&#xff09;将每次走错路的经历都…

Orcale数据表去重创建联合主键

分享一下最近遇到的一个问题&#xff0c;我们从一个数据表中将数据表中的数据同步到另一个数据库的表中&#xff0c;由于要同步的数据表中没有建主键&#xff0c;所以数据同步后发现同步的数据比原始数据表中的数据要多&#xff0c;有不少重复的数据。因此需要对数据表进行去重…

事业编招聘:市委社会工委北京市民政局事业单位公开招聘

市委社会工委市民政局所属事业单位 根据《北京市事业单位公开招聘工作人员实施办法》&#xff08;京人社专技发﹝2010﹞102号&#xff09;等文件精神&#xff0c;北京市委社会工委北京市民政局所属21家事业单位面向社会及应届毕业生公开招聘事业单位工作人员88名。现将具体情况…

【SpringBoot 2.x】定时任务 之- @Scheduled注解

一、概述 Scheduled注解是Spring Boot提供的用于定时任务控制的注解&#xff0c;主要用于控制任务在某个指定时间执行&#xff0c;或者每隔一段时间执行。注意需要 启动类加EnableScheduling实现类加Component方法上加ScheduledScheduled主要有以下几种配置执行时间的方式&…

Neural Discrete Representation Learning (VQ-VAE) 简介

目录VQ-VAE参考VQ-VAE VAE是一种生成模型。 Vector QuantisedVariational AutoEncoder (VQ-VAE)是VAE的变种&#xff0c;其隐含变量是离散的。离散的隐含变量对于自然语言&#xff0c;推理都比较有帮助。著名的DALL-E就使用了类似VQ-VAE的离散隐含变量来从文本生成图像&#x…

【Python】同一网络下,手机和电脑进行socket通信

同一网络下&#xff0c;手机和电脑进行socket通信 最近在学python网络编程&#xff0c;发现socket可以进行跨主机的进程通信&#xff0c;于是尝试用电脑作为服务端&#xff0c;手机作为客户端&#xff0c;来进行socket通信。 电脑端准备 1.电脑开启热点&#xff08;非必须&a…

[附源码]Python计算机毕业设计SSM基于vue的图书管理系统2022(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

java递归实现多级Map集合合并(结合实际场景)

合并Map集合 合并Map集合有很多方法&#xff0c;例如Map自生提供的putAll()方法。但是这种方法只能合并一层的集合&#xff0c;如果是多层的呢&#xff1f; 场景 现在有一个yml配置文件&#xff0c;由于项目部署在多台服务器上&#xff0c;而且每台服务器上的配置有些许差异…

Ajax(四)

1.模板引擎 1.1 模板引擎的基本概念 1.2 什么是模板引擎 不需要再用字符串拼接 1. 3模板引擎的好处 1.4 art-template模板引擎 art-template 是一个简约、超快的模板引擎。 中文官网首页为 http://aui.github.io/art-template/zh-cn/index.html 1.4.1 art-template模板引擎的…

为什么Python现在这么火?

Python可以说是目前最火的网红编程语言&#xff0c;虽然它在近几年在逐渐流行起来&#xff0c;但其实它已经发展了近三十年。那么&#xff0c;为什么Python现在这么火呢&#xff1f;一方面人工智能和大数据的崛起带红了Python&#xff0c;另一方面无论是软件开发者还是非编程工…

抗肿瘤的靶向药物——艾美捷西妥昔单抗Cetuximab说明

近年来恶性肿瘤极大的治疗进展是靶向新药的开发与使用。表皮生长因子受体EGFR是一种具有酪氨酸激酶活性的跨膜受体&#xff0c;受表皮生长因子EGF和转化生长因子-α(TGF-α)的刺激。多种肿瘤细胞株过度表达EGFR&#xff0c;包括25%&#xff5e;80%的结直肠癌CRC细胞。 西妥昔单…