#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语言------学习攻略:深挖指针路线(四)--字符指针变量,数组指针变量,二维数组传参的本质,函数指针变量,函数指针数组


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

相关推荐
C4程序员20 分钟前
北京JAVA基础面试30天打卡14
java·开发语言·面试
黑客影儿1 小时前
Go特有的安全漏洞及渗透测试利用方法(通俗易懂)
开发语言·后端·安全·web安全·网络安全·golang·系统安全
你好,我叫C小白2 小时前
C语言 常量,数据类型
c语言·开发语言·数据类型·常量
小红帽2.02 小时前
从ioutil到os:Golang在线客服聊天系统文件读取的迁移实践
服务器·开发语言·golang
Zafir20243 小时前
Qt实现TabWidget通过addTab函数添加的页,页内控件自适应窗口大小
开发语言·c++·qt·ui
阿巴~阿巴~3 小时前
深入解析C++非类型模板参数
开发语言·c++
计算机小手3 小时前
基于 Flask 与 Milvus 构建高效图片搜索引擎,可通过API接入,支持Docker一键部署
经验分享·docker·flask·开源软件·milvus
阿登林3 小时前
初步学习WPF-Prism
学习·wpf
多吃蔬菜!!!3 小时前
vscode 搭建C/C++开发环境搭建(linux)
linux·c语言·c++
武昌库里写JAVA5 小时前
使用 Java 开发 Android 应用:Kotlin 与 Java 的混合编程
java·vue.js·spring boot·sql·学习