今天我们在上一篇指针进阶的基础上,再来学习一下qsort回调函数
什么是回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
qsort是一个库函数,用来排序,可以直接拿来用,底层使用的快速排序的方法,全称(quick sort)
我们之前的文章讲解过冒泡排序,我们现在就用冒泡排序的代码来复习一下排序的算法
上代码:
#include<stdio.h>
void bubble_sort(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
for (i = 0; i < sz; i++)
{
printf("%d", arr[i]);
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
//排序
//使用冒泡排序的算法来排序
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
return 0;
}
那么这个代码有没什么问题?或者说有什么缺陷?
有的,这种排序的算法,只能用来排序整形数组,如果现在有一个字符串数组,有一个浮点型数组呢,有一个结构体类型的呢?它没办法排序,这就是这个排序的缺点。
那么有没有这样的函数,什么类型都可以排序,那就是我们今天要写的qsort函数。
qsort的好处:
现成的
可以排序任意类型的数据
来看一下qsort的注释和参数。
先来看一下它有几个参数
void qsort(void* base,//指向了待排序数组的第一个元素
size_t num, //待排序数组的元素个数
size_t size, //数组里每个元素的大小,单位是字节
int(* compare)(const void* , const void* ));//指向一个函数,这个函数可以比较两个元素的大小
看第四个参数,是不是我们上一篇提到的函数类型指针
我们知道:
qsort可以用来排序任意数据类型
排序整数类型,>,<,=
排序字符类型,strcmp
排序结构体类型(学生姓名,身高),要指定比较什么,是比较姓名还是身高
所以,qsort的第四个参数就是要我们自己设计函数,想要比较什么类型,就设计比较什么类型的函数,再传参到qsort使用。
#include<stdio.h>
test1()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
//提供一个函数,能够比较两个整数(用整数来举例)的大小
qsort(arr, sz, sizeof(arr[0]), )
}
int main()
{
test1();
return 0;
}
在这段代码中,仔细看我们已经完成了qsort函数前三个参数,还差第四个参数,函数指针,用来排序传进去的数据。
比较大小的函数的代码:
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
注意看,在第三行,我们给p1和p2进行了强制类型转换,这是为什么呢?
因为:
我们这个函数的实参是两个void*类型的参数,而void*类型有两大特点:
它是无具体类型的指针,他可以接收任意类型的地址。
void*类型的指针不可以解引用操作符。
所以我们用了强制类型转换,将void*转化成了int*。
小细节:qsort是默认排成生序的。
测试完了整形数据,我们再来举个例子,排序最抽象的结构体数据
先来构建一个结构体
struct
{
char name[20];
int age;
};
然后完善主函数部分
int main()
{
struct Stu s[]={{"zhangsan",30},{"lsi",25},{"wangwui",50}};
int sz=sizeof(s)/sizeof(s[0]);
qsort(s,sz,sizeof(s),排序函数)
}
可以看到,我们定义了一个结构体类型的数组,然后填了qsort函数的前三个参数,由于结构体里有两个成员,我们就先按照年龄来比较构建一个函数叫cmp_stu_by_age
函数实现
int cmp_stu_by_age(const void* p1, const void* p2)
{
return((struct stu*)p1)->age- ((struct stu*)p2)->age
}
还是强制类型转换没什么好说的,然后指向结构体里的age进行比较
那么还有一种名字比较,大家可以自己试一试,也可以去的我gitee仓库提取