一、回调函数是什么?
如果将函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。在上一篇文章中,转移表实现计算器就是使用了回调函数。
相似的代码可以抽象成函数:有了函数指针后,函数的调用,可以使用函数名来调用,也可以使用函数指针来调用。
二、qsort函数
qsort是专门用来排序的库函数,可以直接排序任何类型的数据。它的底层逻辑是快速排序。
qsort函数的使用需要包含头文件stdlib.h
。
1、排序整型数据
接下来我们看一下qsort函数的原型:
void qsort (void* base,
size_t num,
size_t size,
int (*compar)(const void*,const void*)
);
总共有四个参数:
①void* base
:数组首元素地址。
②size_t num
:数组的元素个数。
③size_t size
:单个元素的大小。
④int (*compar)(const void*,const void*));
:比较函数compar,前-后为升序,后-前为降序。
注:比较函数的参数类型是void*,因此传入参数时要根据参数的类型从而对参数进行强制类型转换。
接下来就使用qsort函数排序整型数据:
#include<stdio.h>
#include<stdlib.h>
//比较函数
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;//升序
}
int main()
{
int arr[10] = { 2,6,7,1,8,9,10,5,4,3 };
int len = sizeof(arr) / sizeof(arr[0]);
qsort(arr, len, sizeof(arr[0]), cmp_int);
for (int i = 0; i < len; i++)
{
printf("%d ",arr[i]);
}
return 0;
}
运行结果:
2、排序结构体数据
定义一个学生的结构体:
struct Stu
{
char name[20];
int age;
};
这里结构体有两个元素,那么该按谁来进行排序呢?
①按照名字排序:比较函数使用strcmp(字符串比较)。
②按照年龄排序:就是上面的整型数据排序。
如下:
#include<stdio.h>
#include<stdlib.h>//qsort函数
#include<string.h>//strcmp函数
struct Stu
{
char name[20];
int age;
};
int cmp_by_age(const void* p1, const void* p2)
{
return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;//升序
}
int cmp_by_name(const void* p1, const void* p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//升序
}
int main()
{
struct Stu arr[3] = { {"zhangsan", 20}, {"lisi", 35}, {"wangwu", 18} };
int len = sizeof(arr) / sizeof(arr[0]);
//按年龄排序
/* qsort(arr, len, sizeof(arr[0]), cmp_by_age);*/
//按名字排序
qsort(arr, len, sizeof(arr[0]), cmp_by_name);
for (int i = 0; i < len; i++)
{
printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);
}
return 0;
}
运行结果:
三、模拟实现qsort函数
我们先来观察冒泡排序的代码:
void bubble_sort(int arr[], int len)
{
for (int i = 0; i <len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
上述冒泡排序可以用来排序整数数据。
那么如果我要改造这个冒泡函数,让他能够排序任意类型的数据,该怎样改造呢?
我们知道两个整型元素可以直接比较大小,但是两个字符串、两个结构体元素是不能直接比较大小的。
那么我们可以把两个元素比较的方法,先封装成函数,然后再把函数的地址传给排序函数,就可以实现排序了。
如下:
#include<stdio.h>
#include<stdlib.h>
//比较函数
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
//一次交换一个字节(可能是字符)
for (int i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//模拟qsort函数
void my_qsort(void* base, size_t len, size_t width, int(*cmp)(const void* p1, const void* p2))//cmp是要传的比较函数的参数
{
//冒泡排序
//躺数
for (int i = 0; i < len; i++)
{
//排一个最大或最小数
for (int j = 0; j < len - i - 1; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//调用比较函数比较前后两个字节(可能是字符),选择升降序
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
int arr[] = { 2,6,7,1,8,9,10,5,4,3 };
int len = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, len, sizeof(arr[0]), cmp_int);
for (int i = 0; i < len; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
这样便实现了对整型数据进行升序的效果。
同样这个模拟qosrt实现的冒泡排序算法也能对结构体元素进行比较,只需要将修改比较函数即可。
如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
};
//比较函数-年龄
int cmp_by_age(const void* p1, const void* p2)
{
return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
//比较函数-姓名
int cmp_by_name(const void* p1, const void* p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
//一次交换一个字节(可能是字符)
for (int i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
//模拟qsort函数
void my_qsort(void* base, size_t len, size_t width, int(*cmp)(const void* p1, const void* p2))//cmp是要传的比较函数的参数
{
//冒泡排序
//躺数
for (int i = 0; i < len; i++)
{
//排一个最大或最小数
for (int j = 0; j < len - i - 1; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//调用比较函数比较前后两个字节(可能是字符),选择升降序
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };
int len = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, len, sizeof(arr[0]), cmp_by_name);
for (int i = 0; i < len; i++)
{
printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);
}
return 0;
}
按名字排序:
传参时传根据年龄的比较函数,就可以按年龄排序: