【C进阶】qsort函数详解
创始人
2024-05-08 22:29:15
0

qsort函数

  • 前言
  • qsort函数
    • (一)引例:简单的冒泡排序
    • (二)qsort函数接收
      • 1.介绍
      • 2.void*
    • (三)使用
      • 1.用qsort实现一个比较整型的函数
      • 2.用qsort实现一个比较结构体的函数
      • 3.用qsort实现一个比较浮点型的函数
    • (四)用冒泡排序模拟qsort函数的使用
      • 1.模拟qsort实现一个比较整数的函数
      • 2.模拟qsort实现一个比较结构体的函数
      • 3.模拟qsort实现一个比较浮点型的函数
  • 总结


前言

在学习回调函数的时候,不免会觉得难以理解或者难以运用,那就需要引入一个例子qsort库函数来让大家理解理解真正的回调函数是怎么样的,这就需要我们进行深入的了解什么是qsort函数,我们是先根据MSDN去查了查什么是qsort,然后就依据它的本质进行演示了不同类型的数的排序,最后模拟了一下冒泡排序运用qsort函数的使用,这都是干货满满!!!


qsort函数

要了解qsort函数,那就需要先引进一个简单的冒泡函数:

(一)引例:简单的冒泡排序

#include//缺陷:只能排序函数
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++) {if (*(arr + j) > *(arr + j + 1)) {int temp = *(arr + j);*(arr + j) = *(arr + j + 1);*(arr + j + 1) = temp;}}}
}
int main() {//对数组进行排序,升序int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);//打印int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}return 0;
}

似乎有点缺陷啊,那我们要排字符呢?排结构体呢?是不是没法用大于号小于号和等于号进行比较了,那我们是不是需要把这个比较的式子单独抽离出来,在单独空间进行比较。

(二)qsort函数接收

1.介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.void*

如果我们用整型指针去接收浮点型指针的时候,出现不兼容的情况,所以qsort的作者也考虑到了这一情况,既然不兼容,那我们找一个典当行,我用什么类型的指针传递给他(什么废铜烂铁都给他),他肯定看看不报错,因为总是有值钱的嘛,那这个典当行就是void*即空类型的指针。

在这里插入图片描述
所以我们为什么要用void*来接收传参呢?
在这里插入图片描述

我们发现float*到int*是不匹配的,那我们就需要void*了。

#includeint main() {int a = 10;float f = 5.5f;int* p = &a;//p = &f;//好处://pp为通用的指针,什么地址都可以放到pp指针里面//void*自己都不知道自己是什么类型的指针void* pp = &f;pp = &a;//弊端://void*指针不能直接用,不能直接进行运算和打印pp++;//err void*未知的大小printf("%f\n", *pp);//err 非法的间接寻址return 0;
}

在这里插入图片描述
这个典当行行长void*说你们可以把不同的指针类型丢给我但是我也有条件的,你给我的什么破铜烂铁啊!我不输出了,你让我加价!?那肯定不可能的,可是我们想要达到我们的目的怎么办呢?我们就需要用暴力手段了,强制类型转换,跟典当行老板说,你今天必须给我加价并报出我满意的价格,不然告你黑店,典当行行长说,好好好,少侠饶命,我肯定加价报出好价格,这下子只要进行强制类型转换就好了。

如下图进行:
在这里插入图片描述

(三)使用

1.用qsort实现一个比较整型的函数

思路是利用qsort函数进行比较函数传参,即cmp_int函数,这个函数就是我们的回调函数,因为它进行传递地址给asort函数,qsort函数接收了cmp_int函数的地址并进行回调,回调以后接受比较的两个元素进行强制类型转换为整型指针进行相加减,根据qsort函数的返回值进行排序升序还是降序。
代码如下:

#include
#include
//实现一个比较整型的函数
int cmp_int(const void* elem1, const void* elem2) {return *(int*)elem1 - *(int*)elem2;//升序//return *(int*)elem2 - *(int*)elem1;//降序
}
//使用qsort函数对数组进行排序,升序
void test1(void) {//对数组进行排序,升序int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);//bubble_sort(arr, sz);//库函数中有一个专门排序的函数叫qsortqsort(arr, sz, sizeof(arr[0]), &cmp_int);//&cmp_int是取出函数的地址并进行回调//打印int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}int main() {test1();//test2();return 0;
}

2.用qsort实现一个比较结构体的函数

思路是跟上面相似,而结构体这边要用的是要定义一个结构体并进行定义结构体数组,这里用到的技巧是结构体指针用的是->,还有一个比较强的技巧是strcmp,这里的strcmp库函数的返回值与qsort函数的返回值完全一致,我们打开MSDN看看:
在这里插入图片描述
结构体内元素类型不同不能进行比较哦,只能进行比较同类型的元素。
代码如下:

#include
#include
#include
//使用qsort函数排序结构体
struct Stu {char name[30];int age;
};
//先按照年龄排序
int cmp_stu_by_age(const void* elem1, const void* elem2) {return ((struct Stu*)elem1)->age - ((struct Stu*)elem2)->age;
}
//再按照名字排序
int cmp_stu_by_name(const void* elem1, const void* elem2) {return strcmp(((struct Stu*)elem1)->name, ((struct Stu*)elem2)->name);
}
void test2(void) {struct Stu s[3] = { {"zhangsan",20 },{"lisi",32},{"wangwu",55} };int sz = sizeof(s) / sizeof(s[0]);//qsort(s, sz, sizeof(s[0]), &cmp_stu_by_age);qsort(s, sz, sizeof(s[0]), &cmp_stu_by_name);int i = 0;for (i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].age);}
}int main() {//test1();test2();return 0;
}

3.用qsort实现一个比较浮点型的函数

#include
#include
#include
int cmp_float(const void* elem1, const void* elem2) {if (*(float*)elem1 > *(float*)elem2){return 1;}else if (*(float*)elem1 < *(float*)elem2){return -1;}else {return 0;}
}
void test3(void) {//对数组进行排序,升序float arr[] = { 0.3, 0.4, 0.1, 0.2, 0.5 };int sz = sizeof(arr) / sizeof(arr[0]);//bubble_sort(arr, sz);//库函数中有一个专门排序的函数叫qsortqsort(arr, sz, sizeof(arr[0]), &cmp_float);//打印int i = 0;for (i = 0; i < sz; i++) {printf("%.1f ", arr[i]);}
}
int main() {//test1();//test2();test3();return 0;
}

(四)用冒泡排序模拟qsort函数的使用

1.模拟qsort实现一个比较整数的函数

先上代码再分析:

#include//改造冒泡排序函数,使用这个函数可以排序任意指定的数组
int cmp_int(const void* elem1, const void* elem2) {return *(int*)elem1 - *(int*)elem2;//升序//return *(int*)elem2 - *(int*)elem2;//降序
}void Swap(char* buf1, char* buf2, int width) {int i = 0;for (i = 0; i < width; i++) {int temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;}
}
//base就是未知参数的起始地址,
//sz就是传过来的数组大小,
//width是传过来数组内一个元素的字节长度
//int (*cmp)(const void*elem1, const void*elem2)是比较方法
//void*的意思就是不知道要传的是什么类型
bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void*elem1, const void*elem2)) {int flag = 1;size_t i = 0;for (i = 0; i < sz - 1; i++) {size_t j = 0;for (j = 0; j < sz - 1 - i; j++) {if (cmp((char*)base+j*width, (char*)base+(j+1)*width) > 0) {//交换Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 0;}}if (flag) {break;}}
}
//使用自己写的bubble_sort排序整型数组
void test3(void) {//对数组进行排序,升序int arr[10] = { 0,9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), &cmp_int);//打印int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}
int main() {//test3();test4();return 0;
}

大家可以点开图片一点一点看,这个图片有点大。
在这里插入图片描述

细节:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.模拟qsort实现一个比较结构体的函数

先上代码再分析:

#include
//使用自己写的bubble_sort排序结构体数组
struct Stu {char name[30];int age;
};
//先按照年龄排序
int cmp_stu_by_age(const void* elem1, const void* elem2) {return ((struct Stu*)elem1)->age - ((struct Stu*)elem2)->age;
}
//再按照名字排序
int cmp_stu_by_name(const void* elem1, const void* elem2) {return strcmp(((struct Stu*)elem1)->name, ((struct Stu*)elem2)->name);
}
void test4(void) {struct Stu s[3] = { {"zhangsan",20 },{"lisi",32},{"wangwu",55} };int sz = sizeof(s) / sizeof(s[0]);bubble_sort(s, sz, sizeof(s[0]), &cmp_stu_by_age);//bubble_sort(s, sz, sizeof(s[0]), &cmp_stu_by_name);int i = 0;for (i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].age);}
}
int main() {//test3();test4();return 0;
}

比较结构体就和前面的qsort函数一样了,只不过换了个名字,因为要用到的都是地址。

3.模拟qsort实现一个比较浮点型的函数

比较浮点型的就需要考虑考虑了,那为了方便起见我们还是比较大小吧,如果相加减精度超过了范围那就不好办了,所以还是直接判断大小更加稳妥一点。

#include
#include
#include
//使用自己写的bubble_sort排序浮点型数组
int cmp_float(const void* elem1, const void* elem2) {if (*(float*)elem1 > *(float*)elem2) {return 1;}else if (*(float*)elem1 < *(float*)elem2) {return -1;}else {return 0;}
}void Swap(char* buf1, char* buf2, int width) {size_t i = 0;for (i = 0; i < width; i++) {int temp = *buf1;*buf1 = *buf2;*buf2 = temp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* elem1, const void* elem2)) {int flag = 0;size_t i = 0;for (i = 0; i < sz - 1; i++) {size_t j = 0;for (j = 0; j < sz - 1 - i; j++) {if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 1;}}if (flag) {break;}}}void test5(void) {//对数组进行排序,升序float arr[] = { 0.1,0.3,0.2,0.4,0.5 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), &cmp_float);//打印int i = 0;for (i = 0; i < sz; i++) {printf("%.1f ", arr[i]);}
}
int main() {//test3();//test4();test5();return 0;
}

总结

总的来讲,qsort函数是很有趣的,当我们一步步去探索,一步步去剖析,会发现很多有趣的库函数,这篇博客细致讲解了qsort函数的使用,先是很细致的讲解了qsort函数的介绍以及使用,再是以一个冒泡排序的思想模拟实现了一个比较不同类型的函数,可谓是干货满满!


客官,码字不易,来个三连支持一下吧!!

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...