一、qsort函数介绍
qsort是一个库函数,可以对任意数据类型的数组进行排序。它的底层是通过快速排序来实现的
qsort
的函数声明:
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
qsort函数的参数:
- void* base
- size_t num
- size_t size
- int(*compar)(const void*,const void*)
qsort函数的返回值:
qsort函数的返回值void型
二、qsort函数参数介绍
2.1:void* base
参数base的类型是void*(空指针),说明base是一个指针,它指向待排序数组的第一个元素,换言之:base存放的是待排序数组的首元素地址
补充:void*类型介绍:
void*是无具体指向的指针类型,任何类型变量的地址都可以存放到void型指针变量里面,这可以说是void*变量的一个优点。当然它也有一个致命的缺点:void*指针不能解引用。
为什么base的类型是void*呢?
回到base的用途,base是用来存放待排序数组首元素的地址,既然存放的是地址,那首先base一定是一个指针类型,那为什么是void型的指针呢?这就要回到qsort函数设计的初衷,qsort函数希望实现对任何类型的数组都可以排序,这就包括:整型数组、字符数组、结构体数组等等,既然面对的排序对象是各种类型的数组,那数组首元素的类型一定也是各种各样的,比如:对整型数组进行排序,数组首元素就是整型;对字符数组排序,数组首元素就是字符型。既然数组首元素的类型可能有多种,那数组首元素的地址一定也是各种各样的,可能是整型的地址,可能是字符的地址,等等。此时就要求base能够存放各种类型的地址,因此就让base是void*型。void*变量就可以存放各种类型的地址。假如:让base是int*型,当待排序的是字符数组,字符数组的首元素是一个字符,字符的地址就不能存到int*的变量里。
2.2:size_t num
num中存的是待排序数组的元素个数,他的类型是size_t,size_t其实就是把unsigned int重命名的得到的。
2.3:size_t size
size中存的是数组中每个元素的大小(以字节为单位)。
2.4:int(* compar)(const void *,const void *)
compar是一个函数指针类型,所谓函数指针就是可以指向一个函数,里面存放的是函数的地址。
compar到底指向什么函数呢?
compar是英文单词compare(比较)的缩写,所以顾名思义,compar是比较的意思,说明它指向一个比较函数。
什么是比较函数?
要实现对数组元素的排序,那一定要对数组里面的元素进行逐一比较,而对于不同类型的数组来说,它们的比较方法也有所不同。比如:整型数组可以比较它们元素之间的大小关系,而字符数组则有专门的字符串比较函数strcmp,如果是一个结构体数组,一个结构体里面有不同的数据,我们就可以按照不同的标准进行排序。就像对一群人进行排序,可以按照年龄排,也可以按照身高排等等。此时就需要用户自己确定一个排序的标准,用函数封装起来,然后把这个函数的地址传给qsort函数用函数指针compar来接收。
对比较函数的要求:
形参compar已经规定了它所指向的函数类型是:int (const void*,const void).即:所指向的比较函数有两个void*类型的参数,函数的返回值是int型。
比较函数的参数、返回值的意义:
int compar (const void* p1, const void* p2);
其中两个void*类型的参数 p1 和 p2 用来存放数组中待比较的两个元素的地址。如果compar函数的返回值小于0,会把p1指向的元素排到p2指向的元素前面;如果返回值等于0,不会改变p1和p2指向的元素位置;如果返回值大于0,会把p1指向的元素排到p2指向的元素后面。
三、实际应用
3.1:利用qsort函数对整型数组排序
int comper(const void* e1, const void* e2) //comper函数是用户自己写的,我们自己当然知道要排序的元素类型是什么 //我们就可以利用强制类型转化来实现对e1和e2的比较 { return *(int*)e1 - *(int*)e2;//void*类型不能直接解引用 } //*(int*)e1 - *(int*)e2<0 //说明e1指向的元素比e2指向的元素小,此时刚好返回一个小于0的数,qsort就会把e1指向的元素排在e2指向的元素前面 //*(int*)e1 - *(int*)e2>0 //说明e1指向的元素比e2指向的元素大,此时刚好返回一个大于0的数,qsort就会把e2指向的元素排到e1指向的元素前面 int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), comper);//利用库函数qsort来排序 int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } //结果: 0 1 2 3 4 5 6 7 8 9
3.2:利用qsort函数对结构体数组排序
按照年龄排序:
struct Stu { char name[20]; int age; }; //根据名字比较 int comper_age(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } int main() { struct Stu arr[3] = { {"zhangsan",100},{"lisi",20},{"wangwu",3} }; int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; //排序前打印 for (i = 0; i < sz; i++) { printf("%s %d ", arr[i].name, arr[i].age); } printf("\n"); //根据年龄进行排序 qsort(arr, sz, sizeof(arr[0]), comper_age); //排序后打印 for (i = 0; i < sz; i++) { printf("%s %d ", arr[i].name, arr[i].age); } printf("\n"); return 0; } //结果: zhangsan 100 lisi 20 wangwu 3 wangwu 3 lisi 20 zhangsan 100
按照名字进行排序:
struct Stu { char name[20]; int age; }; //按照名字比较 int comper_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e1)->name);//名字是字符串,所以名字的比较要用到字符串比较 函数strcmp } int main() { struct Stu arr[3] = { {"zhangsan",100},{"lisi",20},{"wangwu",3} }; int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; //排序前打印 for (i = 0; i < sz; i++) { printf("%s %d ", arr[i].name, arr[i].age); } printf("\n"); //按照年龄进行排序 qsort(arr, sz, sizeof(arr[0]), comper_name); //排序后打印 for (i = 0; i < sz; i++) { printf("%s %d ", arr[i].name, arr[i].age); } printf("\n"); return 0; } //结果: zhangsan 100 lisi 20 wangwu 3 lisi 20 wangwu 3 zhangsan 100