1、qsort()的作用
在我们的日常生活中,排序无处不在。想象一下,当你整理书架时,会按照书籍的类别、作者或者大小进行排列,让你的阅读空间更加整洁有序。又比如,在超市的货架上,商品通常也是按照一定的规则进行排序,方便顾客快速找到所需物品。在学校里,老师可能会根据学生的成绩进行排名,以了解学生的学习情况。
而在计算机编程的世界里,同样需要对数据进行高效的排序。这时,qsort 函数就如同一位强大的秩序之师,挺身而出。qsort 函数是 C 语言标准库中的快速排序函数,它能够快速而有效地对各种数据进行排序。无论是简单的整数数组,还是复杂的结构体数组,qsort 函数都能轻松应对。它以其高效的性能和灵活的用法,成为了程序员们在处理数据排序问题时的得力助手。
说了这么多,接下来我们也该来见一见这位秩序大师的庐山真面目了!
2、qsort函数的基本信息
在cplusplus(C语言函数查询网站)上,我们可以看到qsort函数的函数原型
qsort函数是有4个变量的,分别是base,num,size,comper
他们的类型分别是void*;size_t;size_t;int(*)(const void*,const void*);
cplusplus上对这四个变量的解释:(下图为机翻,因此可能会有些不准确)
对上面的参数解释的理解:
base:base中存放的是待排序数组的第一个元素的地址num:num存放的是base指向的数组的元素个数size:size是base指向的数组中一个元素的长度(以字节为单位)。comper:函数指针—指向了一个比较函数,这个比较函数用来比较数组中的两个元素的大小int(*)(const void* e1,const void* e2)中:
如果e1指向的元素大于e2指向的元素,那么函数返回>0的数字
如果e1指向的元素等于e2指向的元素,那么函数返回0
如果e1指向的元素小于e2指向的元素,那么函数返回<0的数字
3、qsort函数变量的详细介绍
1.void*base
base中存放的是待排序数组的第一个元素的地址
关于base的作用就是为了寻找到待排序的数组的地址,这里为什么使用void*指针呢?
void*指针,可以接收任意类型变量的地址。所以,这里我们使用void*指针来修饰base的目的是为了能够接收任意类型的数组。微软公司在创造这个函数的时候,是不知道使用者将要排序什么类型的数组,所以需要使用void*来修饰。
2.size_t num
num存放的是base指向的数组的元素个数
不管是冒泡排序,还是快速排序,我们都需要知道所要排序的元素个数,这个参数很好理解。
3.size_t size
size:size是base指向的数组中一个元素的长度(以字节为单位)
可能有些人很疑惑,为什么还需要知道数组中一个元素的长度呢?
这是由于base的数据类型是void*
如果我们是int*的话,向后看到的就是整型数组,如果我们是char*的话,向后看到的就是字符数组。但是我们使用的是void*指针,所以我们不知道数组中的元素的具体类型,所以我们需要传递
数组中一个元素的长度,如果排序的是整型的话,一个元素长度是4个字节,如果排序的字符类型的话,一个元素长度是1个字节。
如果没有size参数,给你一个空间,访问十个元素,那么访问一个元素的长度是多大呢?访问十个元素所要占的长度是多少呢?
这就像是在路边询问路线,您需要到达一个地方。有人告诉您向前走十步就能到达,但他们没有说明一步应该走多远。或许他们每步迈的幅度较大,而您迈的幅度较小。因此,当您走了十步之后,可能会发现并没有到达目的地。
我们既需要知道访问多少个元素,也需要知道访问一个元素的长度才能够更加准确的访问空间。
4.int(*comper)(const void*,const void*)
先来分析一下int(*)(const void*,const void*)
首先,该参数是一个函数指针,它指向一个有两个无类型指针作为参数的函数,并且该函数的返回值是int类型的。
该函数指针的作用是将两个无类型指针指向的参数进行比较,如果参数1小于参数2,返回小于0的整数,如果参数1等于参数2,返回0,如果参数1大于参数2,返回大于0的整数。
comper()函数的作用仅仅是比较两个参数的大小,并且通过返回值来告诉我们比较的结果,但是所比较的两个数是不能修改的。使用const修饰。
注意:使用qsort函数排序默认是升序。
当我们得到的返回值>0的值时,qsort函数将会将这两个数字交换,当得到的返回值<0的值时,qsort函数将不会对其进行交换。
4、使用qsort函数排序整型数组,结构体数组
当我们了解了qsort函数的一些基础知识后,可以使用qsort函数来完成一些数据的排序;
4.1 使用qsort函数来排序整型数组:
#include <stdio.h>#include <stdlib.h>int cmp_int(const void* e1, const void* e2){return *(int*)e1 - *(int*)e2;//通过相减来直接获得返回值的大小}void print_arr(int arr[], int sz){int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");}void test1(){int arr[10] = { 3,2,5,7,4,9,0,1,8,6 };//所排数组int sz = sizeof(arr) / sizeof(arr[0]);//元素个数qsort(arr, sz, sizeof(arr[0]), cmp_int);//qsort函数参数的传递print_arr(arr, sz);}int main(){test1();}
return *(int*)e1 - *(int*)e2这里通过相减可以直接获得返回值,如果e1>e2,返回>0的数,如果e1<e2, 返回<0的数,因此,使用qsort函数排序默认为升序,如果想要排逆序,只需要交换e1和e2的位置即可。
4.2 使用qsort函数来排序结构体数组:
//创建结构体变量struct Stu{char name[20];//人名int age;//年龄};void test2(){struct Stu s[3] = { {"zhangsan",18},{"lisi",25},{"wangwu",20} };//创建结构体数组int sz = sizeof(s) / sizeof(s[0]);//获得数组元素个数qsort(s, sz, sizeof(s[0]), cmp_stu);}int main(){test2();}
当开始调用函数cmp_stu来比较结构体数组时,这时候就会产生疑问,该按照什么来比较结构体大小,是按照名字字母顺序来比较?还是按照年龄来比较?
4.2.1 按照年龄来排序结构体数组:
cmp_stu_by_age(const void* e1, const void* e2){return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;}//e1.e2分别指向两个结构体对象
(struct Stu*)e1这是将e1的类型强制转换为struct Stu*结构体类型。
完整代码:
#include <stdio.h>#include <stdlib.h>struct Stu{char name[20];int age;};cmp_stu_by_age(const void* e1, const void* e2){return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;}void test2(){struct Stu s[3] = { {"zhangsan",18},{"lisi",25},{"wangwu",20} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);//qsort函数参数的传递for (int i = 0; i < 3; i++){printf("%s,%d\n", s[i].name, s[i].age);}}int main(){test2();}
4.2.2 按照姓名来排序结构体数组:
在比较前,我们先了解一个函数strcmp函数,该函数的作用是用来比较字符串的大小
使用格式:strcmp(字符串1,字符串2)
如果字符串1 大于 字符串2,返回大于0的数字;
如果字符串1 小于 字符串2,返回小于0的数字;
我们知道名字是字符串,所以不能直接使用>比较,因此我们通过strcmp函数来比较大小
strcmp函数的使用需要包含头文件<string.h>
#include <stdio.h>#include <stdlib.h>#include <string.h>struct Stu{char name[20];int age;};cmp_stu_by_name(const void* e1, const void* e2){return strcmp((*(struct Stu*)e1).name , (*(struct Stu*)e2).name);}void test2(){struct Stu s[3] = { {"zhangsan",18},{"lisi",25},{"wangwu",20} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);//qsort函数参数的传递for (int i = 0; i < 3; i++){printf("%s,%d\n", s[i].name, s[i].age);}}int main(){test2();}