C语言指针(五):回调函数与 qsort 的深层关联

文章目录

[1. 回调函数是什么?](#1. 回调函数是什么?)

[2. qsort 使用举例](#2. qsort 使用举例)

[2.1 qsort函数使用实例](#2.1 qsort函数使用实例)

[2.2 为什么不用冒泡函数](#2.2 为什么不用冒泡函数)

[2.3 使用qsort函数排序整型数据](#2.3 使用qsort函数排序整型数据)

[2.4 使用qsort排序结构数据](#2.4 使用qsort排序结构数据)

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

总结


1. 回调函数是什么?

回调函数就是⼀个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的一方调用的,用于对该事件或条件进行响应。传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能。
代码使用实例:

cpp 复制代码
//回调函数:通过函数指针调用的函数
//使用函数指针可实现该效果

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

void test(int (*pf)(int, int))
{
	printf("%d\n", pf(1, 2));
}

int main()
{
	test(add);//add则为回调函数
	return 0;
}

有了回调函数,再来观察上一篇博客中有关转移表的代码,

复制代码
这是原代码:

//void menu()

//{

// printf("****************************\n");

// printf("****** 1. add 2. sub ******\n");

// printf("****** 3. mul 4. div ******\n");

// printf("****** 0. exit ******\n");

// printf("****************************\n");

//}

//

//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 input = 0;

// int x = 0;

// int y = 0;

// int r = 0;

// do

// {

// menu();

// printf("请选择:");

// scanf("%d", &input);

// switch (input)

// {

// case 1:

// printf("请输入两个操作数:");

// scanf("%d %d", &x, &y);

// r = Add(x, y);

// printf("%d\n", r);

// break;

// case 2:

// printf("请输入两个操作数:");

// scanf("%d %d", &x, &y);

// r = Sub(x, y);

// printf("%d\n", r);

// break;

// case 3:

// printf("请输入两个操作数:");

// scanf("%d %d", &x, &y);

// r = Mul(x, y);

// printf("%d\n", r);

// break;

// case 4:

// printf("请输入两个操作数:");

// scanf("%d %d", &x, &y);

// r = Div(x, y);

// printf("%d\n", r);

// break;

// case 0:

// printf("退出计算器\n");

// break;

// default:

// printf("选择错误,重新选择\n");

// break;

// }

// } while (input);

//

// return 0;

//}

不难发现,没种情况下都有重复代码,且每个功能的函数类型大体上是一致的,可以用一个函数包含这四种函数,再只用回调这个包含函数即可:

复制代码
改造后:

//

//void menu()

//{

// printf("****************************\n");

// printf("****** 1. add 2. sub ******\n");

// printf("****** 3. mul 4. div ******\n");

// printf("****** 0. exit ******\n");

// printf("****************************\n");

//}

//

//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 (*pf)(int, int))

//{

// int x = 0;

// int y = 0;

// int r = 0;

// printf("请输入两个操作数:");

// scanf("%d %d", &x, &y);

// //r = (*pf)(x, y);

// r = pf(x, y);

// printf("%d\n", r);

//}

//

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

2. qsort 使用举例

2.1 qsort函数使用实例

qsort ------ > quick sort

参考网站:cplusplus.com - C++资源网络

cpp 复制代码
	//包含stlib.h
	//void qsort(void* base,   指针 指向被排序数组的第一个元素
	//						   void* 我不知道是什么数组
	//           size_t num,   数组中元素个数 所以是size_t
	//           size_t size,  一个元素的字节大小
	//           int (*compar)(const void*, const void*) 排序函数的指针
	//                                                   因为普适性,要比较各种各样的数据,所                                                                                          以用不同函数来规定排序规则
	//													 比如:比较结构体中数据
	//          );

这就是该函数规定的语法,其中下面这个比较函数需要自己去写,若第一个比较数大于第二个,则返回1;小于返回-1;等于返回0.

具体使用示例看下一个部分


2.2 为什么不用冒泡函数

//1 现成的,不用自己实现
//2 大多数情况下 效率较高
//3 通用

先看一个冒泡函数的例子:

cpp 复制代码
void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = tmp;
			}
		}
	}
}

void print(int arr[], int sz)
{
	for (int 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]);
	//冒泡排序
	bubble_sort(arr,sz);
	print(arr,sz);
	return 0;
}

再用qsort进行排序:

cpp 复制代码
int cmp_int(const void* p1, const void* p2)
{
	if (*(int*)p1 > *(int*)p2)
	{
		return 1;
	}
	else if (*(int*)p1 < *(int*)p2) {
		return -1;
	}
	else
	{
		return 0;
	}
}

void test2()
{
	int arr[] = { 3,1,5,8,7,9,2,4,6,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//qsort
	//包含stlib.h
	void qsort(void* base,   //指针 指向被排序数组的第一个元素
							 //  void* 我不知道是什么数组
	           size_t num,   //数组中元素个数 所以是size_t
	           size_t size,  //一个元素的字节大小
	           int (*compar)(const void*, const void*) //排序函数的指针
	                                                  // 因为普适性,要比较各种各样的数据,//所以用不同函数来规定排序规则
														// 比如:比较结构体中数据
	          );

	//自己写一个比较函数
	qsort(arr,sz,4,cmp_int);

	print(arr, sz);
}

再调用test2()即可。


2.3 使用qsort函数排序整型数据

上面其实已经介绍,这里再完整的介绍一遍:

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

2.4 使用qsort排序结构数据

这⾥需要补充介绍结构指针和结构体成员访问操作符 -> :

cpp 复制代码
//struct stu
//{
//	char name[30];
//	int age;
//};

//void test(struct stu* ps)
//{
//	printf("%s\n", ps->name);
//	printf("%d\n", ps->age);
//}
////->  结构体成员访问操作符,针对结构体指针使用
//
//
//int main()
//{
//	struct stu s = { "zhangsan",20 };
//	test(&s);
//	return 0;
//}

按照结构体中的年龄数据来进行比较:

cpp 复制代码
struct stu
{
	char name[30];
	int age;
};

int cmp_stu_age(const void* p1, const void* p2)
{
	return (*(struct stu*)p1).age - (*(struct stu*)p2).age;//也可用->
}

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

void test3()
{
	struct stu arr[] = { {"zhangsan",20},{"lisi",38},{"wangwu",18} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_age);// 按照年龄
	//qsort(arr, sz, sizeof(arr[0]), cmp_stu_name);
	print_stu(arr, sz);
}

int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

如果按照结构体中的姓名字符串来拍序:

cpp 复制代码
struct stu
{
	char name[30];
	int age;
};


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

//两个字符串比较大小只能使用strcmp
#include <string.h>

int cmp_stu_name(const void* p1, const void* p2)
{
	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}

void test3()
{
	struct stu arr[] = { {"zhangsan",20},{"lisi",38},{"wangwu",18} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//qsort(arr, sz, sizeof(arr[0]), cmp_stu_age); 按照年龄
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_name);
	print_stu(arr, sz);
}

int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

3. qsort函数的模拟实现

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

cpp 复制代码
#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 };
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. 回调函数的本质与应用

    回调函数是通过函数指针调用的特殊函数,其核心是将函数地址作为参数传递给另一个函数,在特定条件下由后者调用以响应事件。

    • 实际场景中,回调函数可有效简化冗余代码。例如计算器程序中,通过将addsub等运算函数的地址传递给calc函数,避免了输入输出逻辑的重复编写,提升了代码复用性与可维护性。
  2. qsort 函数的使用技巧
    qsort是 C 语言标准库中的快速排序函数,支持对任意类型数据排序,其关键在于用户需自定义比较函数(回调函数):

    • 排序整型数据时,比较函数需将void*指针转换为int*,通过差值判断大小;
    • 排序结构体数据时,可根据成员(如年龄、名字)定制比较逻辑,例如用strcmp比较字符串类型的名字。
  3. qsort 的模拟实现思路

    以冒泡排序为基础模拟qsort时,需结合void*指针和回调函数:

    • void*指针可接收任意类型数据,通过强制转换与字节级操作(如char*指针移动)实现通用访问;
    • 自定义比较函数决定排序规则,_swap函数通过字节交换实现任意类型数据的交换,最终实现通用排序功能。
相关推荐
stone51953 小时前
TOTP算法与HOTP算法
c语言·物联网·算法·嵌入式·iot平台·智能门锁
stbomei5 小时前
C 语言判断一个数是否是素数的三种方法文章提纲
c语言·开发语言·算法
etcix14 小时前
wrap cpp variant as dll for c to use
java·c语言·开发语言
papership16 小时前
【入门级-C++程序设计:11、指针与引用-引 用】
c语言·开发语言·c++·青少年编程
胖咕噜的稞达鸭19 小时前
数据结构---关于复杂度的基础解析与梳理
c语言·数据结构·算法·leetcode
范纹杉想快点毕业20 小时前
《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准
服务器·c语言·stm32·单片机·华为·fpga开发·51单片机
長琹21 小时前
9、C 语言内存管理知识点总结
linux·c语言
阿熊不凶1 天前
c语言中堆和栈的区别
java·c语言·jvm
奶黄小甜包1 天前
C语言零基础第16讲:内存函数
c语言·笔记·学习