#C语言——学习攻略:深挖指针路线(五)--回调函数,qsort函数,qsort函数的模拟实现

🌟菜鸟主页:@晨非辰的主页

👀学习专栏: 《C语言学习》

💪学习阶段:C语言方向初学者

⏳名言欣赏:"暴力解法是上帝给的,优化解法是魔鬼教的。"


目录

[1. 回调函数](#1. 回调函数)

[1.1 什么是回调函数](#1.1 什么是回调函数)

[1.2 回调函数试验:改造一般方式实现计算器功能](#1.2 回调函数试验:改造一般方式实现计算器功能)

[2. qsort函数](#2. qsort函数)

[2.1 介绍qsort函数](#2.1 介绍qsort函数)

[2.2 qsort函数排序整型数据](#2.2 qsort函数排序整型数据)

[2.3 qsort函数排序结构体数据](#2.3 qsort函数排序结构体数据)

[3. qsort函数模拟实现](#3. qsort函数模拟实现)


1. 回调函数

1.1 什么是回调函数

--简单说,回调函数就是由一个通过函数指针调用的函数。

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

--回调函数的一大特点回调函数简化代码避免重复代码 ------>如果多个函数需要类似的逻辑,回调可以提取公共部分,减少重复。

--就比如上一篇博客分享的:一般方式实现计算器功能的代码,就可以使用回调函数,来看一下吧:

cpp 复制代码
int Add(int x, int y)//这里的Add就是回调函数
{
	return x + y;
}

void test(int(*pf)(int, int))//参数时函数指针,因为要接收函数地址
{
	int r = pf(10, 20);
	printf("%d\n", r);
}

int main()
{
	test(Add);//将Add函数的地址传给test函数
	return 0;
}

1.2 回调函数试验:改造一般方式实现计算器功能

--在前面的博客有分享怎么一般方式来实现计算器的基本功能,但是部分代码重复较高,今天就用回调函数来优化:

cpp 复制代码
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(*pa)(int, int))
{
	int ret = 0;
	int x = 0;
	int y = 0;
	printf("请输入操作数:");
	scanf("%d %d", &x, &y);
	ret = pa(x, y);
	printf("%d\n", ret);
}

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

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

--看完这串代码,可以知道:里面的像add、sub、mul、div这四个函数都是回调函数

--从图中可以清楚的看到,add等函数将地址传给calc函数。calc函数的参数是函数指针用来接收地址,在内部调用函数。

--这样就看到了回调函数到底用来做什么:把调用的函数的地址以参数形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数。


2. qsort函数

2.1 介绍qsort函数

-- qsort 是C标准库(<stdli.h>)提供的快速排序(Quick Sort)函数,用于对任意类型的数组进行排序。

--优点:

1. 通用性强(支持任意数据类型): 2. 灵活性高(自定义排序规则)

基本类型(int, float, char) 升序 / 降序

指针类型(char*, void*) 按字符串长度排序

结构体(struct) 多关键字排序(如先按年龄,再按姓名)

动态分配的数组

--qsort函数返回类型、参数:

cpp 复制代码
void qsort(
    void *base,     ------>指向要排序的数组首元素的指针。
    size_t nmemb,   ------>数组元素个数
    size_t size,    ------>每个元素的字节大小
    int (*compar)(const void *, const void *)  ------>比较函数指针,用于比较数组中的两个元素
);

--对于函数参数为两个void,可以接收任意类型的指针。

返回值

<0a 应排在 b 前面(升序)。

=0ab 相等。

>0a 应排在 b 后面(降序)。

2.2 qsort函数排序整型数据

cpp 复制代码
//升序,cmp_int1用来比较两个整形数据
int cmp_int1(const void* p1, const void* p2) 
{
	return (*(int*)p1) - (*(int*)p2);
	//void*指针不能直接进行解引用,强转为整型指针类型
	//默认升序是因为 a - b 的写法符合直观的"从小到大"逻辑
}
//降序
int cmp_int2(const void* p1, const void* p2)
{
	return (*(int*)p2) - (*(int*)p1);//反转逻辑
}
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0;i < sz;i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = { 3,1,5,8,7,9,2,4,6,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print_arr(arr, sz);
	//使用qsort函数的,需要自己写一个比较函数
	qsort(arr, sz, sizeof(arr[0]), cmp_int1);
	printf("升序:");
	print_arr(arr, sz);
	qsort(arr, sz, sizeof(arr[0]), cmp_int2);
	printf("降序:");
	print_arr(arr, sz);
}

2.3 qsort函数排序结构体数据

cpp 复制代码
#include<string.h>
#include<stdlib.h>
struct Stu
{
	char name[30];
	int age;
};

//按年龄比较
int cmp_stu_by_age(const void* p1, const void* p2)
{
	return (((struct Stu*)p1)->age - ((struct Stu*)p2)->age);
}
//按名字
int cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
	//strcmp是专门用来比较两个字符串的大小的
}

void print_stu(struct Stu arr[], int sz)
{
	int i = 0;
	for (i = 0;i < sz;i++)
	{
		printf("%s:%d\n", arr[i].name, arr[i].age);
	}
	printf("\n");
}

//按照年龄来排序 
void test2()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
	print_stu(arr, sz);
}

//按照名字来排序 
void test3()
{
	struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
	print_stu(arr, sz);
}
int main()
{
	test2();
	test3();
	return 0;
}

3. qsort函数模拟实现

--使用回调函数,模拟实现qsort(采用冒泡的方式)。(首次使用void*类型)

cpp 复制代码
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 };
	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;
}

往期复习:

1. #C语言------学习攻略:深挖指针路线(一)--指针变量、地址、意义与指针运算

2. #C语言------学习攻略:深挖指针路线(二)--const修饰、野指针分析、断言和指针的作用

3. #C语言------学习攻略:深挖指针路线(三)--数组与指针的结合、冒泡排序

4.#C语言------学习攻略:深挖指针路线(四)--字符指针变量,数组指针变量,二维数组传参的本质,函数指针变量,函数指针数组


**结语:本篇内容就到这里了,主要分享了指针变量类型的一些内容,后续仍会分享指针的相关知识;指针的内容需要反复研读 ,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,**你这么帅、这么美给个三连吧~~~

相关推荐
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨7 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117769 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao9 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
人邮异步社区9 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai10 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang