qsort函数的第四个参数是一个函数指针,指向的是一个程序员根据需要排序的数组类型来写的函数 。
正因为如此,qsort才能实现对不同的数据类型进行灵活排序
1.2.2.1 qsort函数对浮点型数组排序实例
#include#includeint SortByDouble(const void* e1, const void* e2){return *(double*)e1 - *(double*)e2;}int main(){double arr[] = { 9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), SortByDouble);return 0;}
注意:void*可以接受任何指针类型,但是不可以对其进行解引用操作 , 但是可以对其接受到的指针类型进行强制类型转换,即如上自定义函数中的部分 。
1.2.2.2 qsort函数对结构体数组排序实例
#include#include#includestruct stu{char name[8];int age;};int SortByAge(const void* e1, const void* e2){return ((struct stu*)e1)->age - ((struct stu*)e2)->age;}int SortByName(const void* e1, const void* e2){return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);}int main(){struct stu s[] = { {"zhangsan",20},{"lisi",19},{"wangwu",18} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), SortByAge);qsort(s, sz, sizeof(s[0]), SortByName);return 0;}
结构体就更加复杂了,而且需要小心算结构体数组元素个数和每个元素大小的时候都是不管结构体的成员变量 。
所以千万不要以为自己是在对结构体中的年龄进行排序,而计算数组大小的时候用了:
int sz = sizeof(s)/sizeof(s[0].age);//错误实例不应该去让s[0]访问到age
还有一点是结构体定义一定要放在最前面,放错位置了,都会导致自定义函数显示未定义的错误
1.2.3 利用冒泡排序算法实现qsort函数的模拟
其实qsort函数的算法并不是冒泡排序 , 但我为什么说是模拟实现qsort呢?
请看下图:
发现了吗?我写的这个函数的参数简直就是和qsort函数的参数异曲同工之妙啊~
所以接下来我需要实现的就是对函数进行定义了 。
如下是大体的框架:
比较难的是如何设置cmp函数和swap函数内的参数 。
cmp函数接收的是两个相邻元素的地址,但是元素的类型可以有很多种,且所占字节大小也不同,这就为我们设下了一个难题 。
但是我们知道char类型所占字节是最小的,我们是不是可以用它来表示所有变量呢?
所以先将首元素地址强制类型转换成char*,再对其加某个值,可以让其向后移动而指向不同的元素 。
那么加上什么值才合适呢?
这是位于内部循环,j一直在发生变换,我们又知道每个元素所占字节大?。?所以根据上述推论,我们有以下结果 。
if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0
接下来是swap函数内部的参数
swap函数是满足了前者大于后者 , 就对这两个数进行交换 。
swap函数的参数应该是两个元素的地址,并且是和cmp()函数中的参数一样 , 所以有如下结果:
swap((char*)base + j * size, (char*)base + (j + 1) * size)
大体搭构好了,接下来就是实现Swap函数了
文章插图
等一下,这样的Swap函数真的对吗?如果传过去的是两个元素的地址,我们真的能成功交换他们吗?
答案是否定的,因为我们平常写的交换两元素的函数是这样的
Swap(char* buf1, char* buf2){while((*buf1)&&(*buf2)){int tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}}
发现问题了吗?我们每次++的时候都已经确定这个指针会跳过几个字节,而现在我们写的Swap函数可并不知道自己参数的类型,所以不能保证每次++后跳过的字节都正确 。
讲了半天 , 我们最需要的是知道我们交换的元素所占字节大小是多少
所以需要给Swap函数增加一个参数size
修改后:
BubbleSort(void* base, int count, int size, int (*cmp)(const void*, const void*)){int i = 0;for (i = 0; i < count - 1; i++){int j = 0;for (j = 0; j < count - 1 - i; j++){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//比较大小{Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);//交换两元素}}}}
Swap函数的实现
void Swap(void* buf1, void* buf2, int size){int i = 0;for (i = 0; i < size; i++)//每次循环交换两个元素的一个字节{char tmp = *((char*)buf1 + i);*((char*)buf1 + i) = *((char*)buf2 + i);*((char*)buf2 + i) = tmp;}}
每次循环交换两个元素的一个字节 。
直至把一个元素所占的字节全部交换完成 。
这样我们就把整个qsort函数的模拟实现了!
最后附上源码:
#include#include#includeint SortByInt(const void* e1, const void* e2){return *(int*)e1 - *(int*)e2;}void Swap(void* buf1, void* buf2, int size){int i = 0;for (i = 0; i < size; i++){char tmp = *((char*)buf1 + i);*((char*)buf1 + i) = *((char*)buf2 + i);*((char*)buf2 + i) = tmp;}}BubbleSort(void* base, int count, int size, int (*cmp)(const void*, const void*)){int i = 0;for (i = 0; i < count - 1; i++){int j = 0;for (j = 0; j < count - 1 - i; 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[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);BubbleSort(arr, sz, sizeof(arr[0]), SortByInt);return 0;}
【爆肝!回调函数的实用案例,建议收藏~(计算器改良,qsort快排函数应用实例】妹有开源精神是不行的![doge]
- v vlookup函数的使用方法
- wps怎么查找重复项 wps查找重复项的函数
- 怎么判断导函数的正负 如何知道导函数是正是负
- 二、类的六个默认成员函数
- C++ 构造函数与析构函数,与成员初始化列表语法
- 你必须要学会的js构造函数、原型、原型链
- 1、激活函数与损失函数选择
- printf 函数转换说明完整格式详解
- 欧拉函数、筛法求欧拉函数
- 2