第十二讲:指针(4)

第十二讲:指针(4)

  • 1.回调函数
    • 1.1什么是回调函数
    • 1.2深入理解并使用回调函数
      • 1.2.1简单写法
      • 1.2.2优化
  • 2.qsort函数详解
    • 2.1函数简单介绍
    • 2.3qsort函数使用举例
      • 2.3.1qsort函数排序整形数据
      • 2.3.2qsort函数排序结构数据
  • 3.qsort函数的模拟实现
    • 3.1冒泡排序
    • 3.2冒泡排序的改造

总结:该篇博客详细对以下两个内容进行讲解:
1.回调函数
2.qsort函数

1.回调函数

1.1什么是回调函数

回调函数就是一个通过函数指针调用的函数

如果我们将函数的地址作为参数传递给另一个函数,当通过这个指针被用来调用其指向的函数时,被调用的函数就是回调函数,回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件进行响应
下面我们通过代码来理解什么是回调函数:

1.2深入理解并使用回调函数

我们写一个简易的计算器

1.2.1简单写法


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(" 0:exit \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;
}

这个方法写的十分冗余,下面我们来进行优化

1.2.2优化


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 calc(int (*pa)(int, int)) //使用函数指针变量来接受函数指针
{
	printf("请输入两个操作数:\n");
	int x, y;
	x = 0;
	y = 0;
	scanf("%d %d", &x, &y);
	int set = pa(x, y);
	printf("%d\n", set);
}

int main()
{
	int input = 1;
	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:
			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;
}

我们通过画图来理解回调函数:
在这里插入图片描述

在这里插入图片描述

2.qsort函数详解

2.1函数简单介绍

这是一个执行快速排序的函数,包含在<stdlib.h>库中

函数原型:


void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

对各个参数进行介绍如下:

1.base:指向要排序数组的首元素的地址
2.num:数组中的元素的个数
3.width:数组中每个元素的大小,单位是字节
4.int (__cdecl *compare )(const void *elem1, const void *elem2 ):
这是一个由编程者自己写的函数,将函数地址传入,函数的形式被规定,该函数定义了如何比较两个元素

对于自己定义的这个函数,通过其返回值决定所排序元素的顺序,遵循以下原则 :
在这里插入图片描述

2.3qsort函数使用举例

2.3.1qsort函数排序整形数据


//使⽤qsort函数排序整型数据

int Sort1(const void* pa, const void* pb)
{
	return (*(int *)pa - *(int*)pb);
}

int main()
{
	int arr[] = { 2,3,4,5,1,6,7,9,10 };

	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), Sort1); //void qsort(void* base, 
	        //size_t num, 
			//size_t width, 
			//int(__cdecl * compare)(const void* elem1, const void* elem2));
	Print(arr, sz);
	return 0;
}

2.3.2qsort函数排序结构数据


//使⽤qsort函数排序结构数据

//创建一个结构类型,假设为学生数据
struct Stu
{
	char name[20];
	int age;
};

//按照名字排序
int SortByName(const void* pa, const void* pb)
{
	return (strcmp(((struct Stu*)pa)->name, ((struct Stu*)pb)->name));
}

//按照年龄排序
int SortByAge(const void* pa, const void* pb)
{
	return (((struct Stu*)pa)->age - ((struct Stu*)pb)->age);
}

int main()
{
	//给定学生数据
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };

	int sz = sizeof(arr) / sizeof(arr[0]);
	//按照名字排序
	qsort(arr, sz, sizeof(arr[0]), SortByName); //void qsort(void* base, 
										   //size_t num, 
										   //size_t width, 
										   //int(__cdecl * compare)(const void* elem1, const void* elem2));
	//按照年龄排序
	qsort(arr, sz, sizeof(arr[0]), SortByAge); //void qsort(void* base, 
									       //size_t num, 
										   //size_t width, 
										   //int(__cdecl * compare)(const void* elem1, const void* elem2));

	return 0;
}

3.qsort函数的模拟实现

我们使用冒泡排序来进行qsort函数的模拟实现,首先先介绍一下什么是冒泡排序

3.1冒泡排序

目前的排序方式有很多,快速排序,希尔排序,冒泡排序,我们现在使用冒泡排序进行类比排序


void BubbleSort(int* pa, int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (*(pa + j) > *(pa + j + 1))
			{
				int temp = *(pa + j);
				*(pa + j) = *(pa + 1 + j);
				*(pa + 1 + j) = temp;
			}
		}
	}
}

void Print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
		printf("%d ", *(arr + i));
}

int main()
{
	int arr[] = { 2,3,4,5,1,6,7,9,10 };

	//冒泡排序
	int sz = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, sz);

	//进行打印
	Print(arr, sz);
	return 0;
}

3.2冒泡排序的改造


//冒泡函数的改造
void Change(char* pa, char* pb, int sz)
{
	for(int i = 0; i<sz; i++)
	{
		char temp = *pa;
		*pa = *pb;
		*pb = temp;
		pa++;
		pb++;
	}
}

void BubbleSort(void* pa, size_t sz, size_t width, int(* compare)(const void* pa, const void* pb))
{
	for (int i = 0; i < sz - 1; i++)  //尽管是改造,但是这两个for循环是不用改变的
	{                                 //第一个for循环表示循环次数
		for (int j = 0; j < sz - 1 - i; j++) //第二个for循环是将两个数进行比较
		{
			//仍然是判断,如果compare函数返回值大于0,进行交换
			if (compare((char*)pa + j * width, (char*)pa + (j + 1) * width) > 0)
				Change((char*)pa + j * width, (char*)pa + (j + 1) * width, width); //进行交换,但是这个交换一次要交换数组类型个字节
		}
	}
}

int main()
{
	//给定学生数据
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };

	//冒泡排序
	int sz = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, sz, sizeof(arr[0]), SortByName);  //void qsort(void* base, 
										              //size_t num, 
										              //size_t width, 
										              //int(__cdecl * compare)(const void* elem1, const void* elem2));
	BubbleSort(arr, sz, sizeof(arr[0]), SortByAge);
	return 0;
}

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

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

相关文章

【投稿优惠|快速见刊】2024年能源资源与材料应用国际学术会议(ICERMA 2024)

全称&#xff1a;【投稿优惠|快速见刊】2024年能源资源与材料应用国际学术会议(ICERMA 2024) 会议网址:http://www.icerma.com 会议时间: 2024/2/29 截稿时间&#xff1a;2024/2/20 会议地点: 长沙 投稿邮箱&#xff1a;icermasub-conf.com 投稿标题&#xff1a;ICERMA 2024Art…

esp32-使用UDP控制电机(七)

目录 前言 端口配置 代码 前言 本文基于esp32使用platformio平台&#xff0c;通过udp来控制电机运行。 关键词&#xff1a;platformio&#xff0c;freertos&#xff0c;upd&#xff0c;esp32&#xff0c;motor&#xff0c; 端口配置 采用drv8833驱动板&#xff0c;其中es…

IDEA buid一直不能完成,无法运行

问题如下所示&#xff1a; 解决方案 output 路径不对&#xff0c;正确路径&#xff1a;项目目录\target\classes

【JAVA入门】Day04 - 方法

【JAVA入门】Day04 - 方法 文章目录 【JAVA入门】Day04 - 方法一、方法的格式1.1 无参无返回值的方法定义和调用1.2 带参数的方法定义和调用1.3 形参和实参1.4 带返回值的方法定义和调用1.5 方法的注意事项 二、方法的重载三、方法的使用四、方法的内存原理4.1 方法调用的基本内…

3W 3KVAC隔离 宽电压输入 AC/DC 电源模块——TP03AL系列

TP03AL系列产品具有交直流两用、输入电压范围宽、高可靠性、低功耗、安全隔离等优点。广泛适用于工控和电力仪器仪表、智能家居等对体积要求苛刻、并对EMC 要求不高的场合&#xff0c;如果需要应用于电磁兼容恶劣的环境下必须添加EMC 外围电路。

保研机试之【x86/x86-64体系结构中的寄存器】

先来看一下这六个选项的功能&#xff1a; 举一个例子&#xff1a; 对于CR2寄存器和中断向量表&#xff1a; 也就是先通过CR2寄存器找到引发错误的虚拟地址&#xff0c;然后操作系统分析错误原因&#xff0c;通过IDTR寄存器找到IDT&#xff08;中断向量表&#xff09;&#xff0…

Rust 中的mod 使用

1、本文将展示在Rust语言中如何引入模块。 2、项目目录如下图。 2.1、mod.rs中是需要引入的模块代码。 2.2、main.rs和文件夹utils在src文件夹下。 2.3、mod.rs代码如下。 pub mod nation{pub mod government{pub fn govern(){let aString::from("govern");println…

代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯 文章目录 代码随想录算法训练营Day 38| 动态规划part01 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯理论基础一、常规题目二、解题步骤…

代码随想录算法训练营第二十七天|​回溯法理论基础​、第77题. 组合

理论基础 回溯法基本介绍 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。递归函数的下面就是回溯的逻辑 因为回溯的本质是穷举&#xff0c;穷举所有可能&#xff08;暴力法&#xff09;&#xff0c;然…

Today At Apple 2024.04.15 Phone15 入门

官网&#xff1a; https://www.apple.com/today/Apple 亚洲第一大商店&#xff1a;Apple 静安零售店现已在上海开幕如下预约课程&#xff1a;下载 Apple Store&#xff08;不是app store&#xff09;&#xff0c;点击课程预约笔记&#xff1a;Today At Apple Notes果粉加群 &am…

并发编程总结(二)

目录 Java 对象头 wait / notify sleep(long n) 和 wait(long n) 的区别 死锁 定位死锁 饥饿 ReentrantLock Java 对象头 以 32 位虚拟机为例 64 位虚拟机 Mark Word 在程序中查看对象结构&#xff1a; 导入依赖&#xff1a; <!-- https://mvnrepository.com/artifac…

掌握这个Jenkins插件,离测试开发又近一步!

Jenkins Pipeline是一种可编程的、可扩展的持续交付管道&#xff0c;允许您使用脚本来定义整个软件交付过程。 以下是使用Jenkins Pipeline创建和配置流水线的基本步骤。 Part 01. 创建一个Pipeline Job 在Jenkins中创建一个新的"Pipeline"类型的Job。 以下是在J…

解决宝塔Nginx和phpMyAdmin配置端口冲突问题

问题描述 在对基于宝塔面板的 Nginx 配置文件进行端口修改时&#xff0c;我注意到 phpMyAdmin 的端口配置似乎也随之发生了变化&#xff01; 解决方法 官方建议在处理 Nginx 配置时&#xff0c;应避免直接修改默认的配置文件&#xff0c;以确保系统的稳定性和简化后续的维护…

简单问题汇总

一、vector和list 1.vector vector是可变大小数组的序列容器&#xff0c;拥有一段连续的内存空间&#xff0c;并且起始地址不变&#xff0c;因此能高效的进行随机存取&#xff0c;时间复杂度为o(1)&#xff1b;但因为内存空间是连续的&#xff0c;所以在进行插入和删除操作时…

触摸OpenNJet,云原生世界触手可及

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 导言OpenNJet云原生引擎介绍云原生平台的介绍优化与创新 为什么选择OpenNJet云原生引擎如何在windo…

【MATLAB源码-第207期】基于matlab的单相光伏并网系统仿真,并网策略采用基于扰动观测法的MPPT模型和使用电压电流双闭环SPWM控制。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 本文将重点分析光伏发电最大功率点跟踪&#xff08;MPPT&#xff09;技术和逆变器的并网控制技术&#xff0c;并在Simulink环境下建立模拟系统&#xff0c;以体现这些技术的应用与效果。文章结构如下&#xff1a;首先简介光伏…

class常量池、运行时常量池和字符串常量池的关系

类常量池、运行时常量池和字符串常量池这三种常量池&#xff0c;在Java中扮演着不同但又相互关联的角色。理解它们之间的关系&#xff0c;有助于深入理解Java虚拟机&#xff08;JVM&#xff09;的内部工作机制&#xff0c;尤其是在类加载、内存分配和字符串处理方面。 类常量池…

【java9】java9新特性概述

经过4次的跳票&#xff0c;历经曲折的Java9最终在2017年9月21日发布。因为里面加入的模块化系统&#xff0c;在最初设想的时候并没有想过那么复杂&#xff0c;花费的时间超出预估时间。距离java8大约三年时间。 Java9提供了超过150项新功能特性&#xff0c;包括备受期待的模块…

sql注入---sqli靶场

1.什么是SQL注入 SQL注入是比较常见的网络攻击方式之一&#xff0c;它不是利用操作系统的BUG来实现攻击&#xff0c;而是针对程序员编写时的疏忽&#xff0c;通过SQL语句&#xff0c;实现无账号登录&#xff0c;甚至篡改数据库 2.sql注入原理 攻击者注入一段包含注释符的SQL语…

软件2班20240513

第三次作业 package com.yanyu;import java.sql.*; import java.util.ResourceBundle;public class JDBCTest01 {public static void main(String[] args) {ResourceBundle bundle ResourceBundle.getBundle("com/resources/db");// ctrl alt vString driver …