当前位置: 首页 > news >正文

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

文章目录

1. 回调函数是什么?

2. qsort 使用举例

2.1 qsort函数使用实例

2.2 为什么不用冒泡函数

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

2.4 使用qsort排序结构数据

3. qsort函数的模拟实现

总结


1. 回调函数是什么?

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

	//包含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 通用

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

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进行排序:

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.hvoid qsort(void* base,   //指针 指向被排序数组的第一个元素//  void* 我不知道是什么数组size_t num,   //数组中元素个数 所以是size_tsize_t size,  //一个元素的字节大小int (*compar)(const void*, const void*) //排序函数的指针// 因为普适性,要比较各种各样的数据,//所以用不同函数来规定排序规则// 比如:比较结构体中数据);//自己写一个比较函数qsort(arr,sz,4,cmp_int);print(arr, sz);
}

再调用test2()即可。


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

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

#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排序结构数据

这⾥需要补充介绍结构指针和结构体成员访问操作符 -> :
//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;
//}

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

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;
}
如果按照结构体中的姓名字符串来拍序:
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* 的作⽤。
#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函数通过字节交换实现任意类型数据的交换,最终实现通用排序功能。
http://www.dtcms.com/a/333578.html

相关文章:

  • 前端性能优化
  • JCTools 无锁并发计数器:ConcurrentAutoTable
  • obsidian ai/copilot 插件配置
  • epoll边缘模式收数据学习
  • 【100页PPT】数字化转型某著名企业集团信息化顶层规划方案(附下载方式)
  • 基于之前的Python附魔插件做出的一些改进
  • 3s岗位合集
  • 并行Builder-输出型流程编排的新思路
  • AI提高投放效率的核心策略
  • 【生产实践】内网YUM源中rpm包的替换与仓库升级实战
  • 应用侧华为云LoTDA设备接入平台
  • 2025二建成绩公布!各地合格标准汇总!
  • 通俗易懂:Vue3的ref()运行机理
  • Windows Server存储智能数据校验
  • AMQP协议介绍
  • 【进阶】Java技术栈八股文学习资料整理
  • 优化网络ROI:专线复用,上云出网一“线”牵!
  • 力扣top100(day04-04)--栈
  • 从“写代码”到“定义需求”:AI编程工具如何重构软件开发的核心流程?
  • 深度学习-卷积神经网络-ResNet 残差网络
  • 永磁同步电机控制 第二篇、电机的分类
  • 支持向量机的原理和案例解析
  • Sklearn 机器学习 手写数字识别 使用K近邻算法做分类
  • Android Studio
  • IO流-转换流
  • MySQL的分析查询语句(EXPLAIN):
  • stream流debug
  • 华硕主板怎样调整风扇转速
  • Redis高级优化实战:从键值设计到集群调优
  • [HDCTF 2023]Normal_Rsa(revenge)