qsort相信大家都已经很熟悉了,他是一个快速排序函数,由于其形参类型为void类型,故而可以对多种数据类型进行排序,而不仅仅针对整型数据的排序,应用广泛而方便,所以掌握qsort快排可精简代码量,提高开发效率。下面我们首先看一下它的函数原型和相关参数的含义:
函数原型:void qsort(void *base,size_t num,size_t size,int(*compare)(const void*p1,const void*p2));
参数解析:
1、 qsort为函数名,而函数名前面的void,说明该函数无需返回值,仅仅作为排序使;
2、 void *base ,表示待排序数据的其实地址(无论是排序整型还是字符串又或者是结构体类型的数据,一般都是将这些数据封装成数组来排序,所以该参数一般为数组名,ps这里有个小知识点:数组名表示该数组的地址或者数组首元素的地址);
3、 size_t num ,表示待排序数据的个数(一般为封装后的数组元素个数);
4、 size_t size ,表示待排序数据的单个数据元素的大小,也就是数组元素的大小;
5、int(*compare)(const void*p1,const void*p2), 这个参数为qsort函数的核心参数,我们来重点解析一下
首先(*compare)表明这个参数是一个函数指针,该指针指向的函数是一个返回值为int类型,且参数为const void*p1,const void*p2 的函数;我们习惯性的将这个函数称为比较函数,通过函数指针的定义我们可以到推导出这个比较函数原型为 int compare(const void*p1,const void*p2);
关于这个比较函数int compare(const void*p1,const void*p2),我们要特别留意它的形参类型,为两个指针!
下面我们来看一下用qsort来排序字符串的应用实例以及相关细节的解释;
头文件和主函数:
#include<stdio.h>
#include<string.h> //由于要比较字符串的大小需调用strcmp库函数,所以需要包含这个头文件
int main()
{
int i;
char *p[]={"xiyangyang","meiyangyang","lanyangyang","feiyangyang"};//待排序字符串
int sz=sizeof(p)/sizeof(p[0]);//元素个数
qsort(p,sz,sizeof(p[0]),compare);
for(i=0;i<sz;i++)//循环输出数组
{
printf("%s\n",p[i]);
}
return 0;
}
比较函数:
int compare(const void *p1,const void *p2) //比较函数
{
return strcmp(*(char**)p1,*(char**)p2); //库函数比较字符串大小,按照ascII码表排序
}
回到我们的主题,为什么这里要用(char**)进行强制转换?而不是按照整型排序的(int*)那样直接使用(char*)?
首先我们看一下比较函数传过来的形参p1 p2,假设他对应字符串数组的p[0]和p[1],字符串数组的本质是一个指针数组,p[0]和p[1]分别为一个指针,各自指向一个字符串的开头,比如p[0]指向“xiyangyang”,这里可以看见p[0]可以看成一个一维字符数组的指针,那么p[0]就是这个一维数组的数组名,指向的就是这个一维字符数组的qishi地址或者首元素地址,而数字名只有在sizeof(数组名)和&数组名是才代表整个数组,所以这里如果只进行(char*)强转,比较函数内部调用的库函数就会变成这样:
strcmp(*(char*)p1,*(char*)p2); 这里p1(假设为p[0]),p2(假设为p[1]),强转一次再解引用一次得到的只是单个字符'x'和'm',这个参数就不符合strcmp的要求了,因为strcmp()的形参也需要为2个指针!而使用char**就可以解决这个问题,首先char*将指针强转为字符指针类型,再加一个*保证他是一个二级指针(字符串数组就相当于是一个二级数组),这样解引用一次后p1,p2仍然为指针,这样就符合strcmp的参数要求了;
下面是排序后的运行结果。
feiyangyang
lanyangyang
meiyangyang
xiyangyang