介绍qsort函数之前,我们先了解一下什么是回调函数??
回调函数就是⼀个通过函数指针调⽤的函数。如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。qsort函数就是根据回调函数实现的。
接下来介绍一下qsort函数:
1.编译器函数库自带的快速排序函数。
2.使用qsort()排序并用 bsearch()搜索是一个比较常用的组合,使用方便快捷。
3.qsort 的函数原型是:void qsort(void*base, size_t num, size_t width, int(__cdecl*compare)(const void*, const void*));
各参数:1 待排序数组首地址 ;2 数组中待排序元素数量 ;3 各元素的占用空间大小; 4 指向函数的指针(待定函数的比较方法)
int(__cdecl*compare)()就是回调函数,将函数本身作为参数传递给对应的方法,然后在特定的时候再调用。
现在我们模仿qsort的功能实现一个通用的冒泡排序:
#include<stdio.h>
#include<windows.h>
#include<assert.h>
//回调函数
int int_cmp(const void *p1, const void *p2)
{
int *_p1 = (int *)p1;
int *_p2 = (int *)p2;
if (*_p1 < *_p2)
{
return 1;//降序
}
else if (*_p1 > *_p2)
{
return -1;
}
else
{
return 0;
}
}
//交换
void swap(char *x, char *y, int width)
{
while (width--)
{
*x^=*y;
*y^=*x;
*x^=*y;
x++;
y++;
}
}
//实现qsort
void my_qsort(void *arr, int num, int width, int(*cmp)(void *, void *))
{
assert(arr);
assert(cmp);
int i = 0;
for (; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//调用函数判断两个操作数大小。
//cmp()传参只需将操作数的地址传过去。
if (cmp((char *)arr + j*width, (char *)arr + (j + 1)*width)>0)
//这里是任意类型,所以不能整体交换,只能局部按字节交换,达到整体交换的目的。
{
swap((char *)arr + j*width, (char *)arr + (j + 1)*width, width);
}
}
}
}
int main()
{
int arr[] = { 1, 5, 4, 6, 8, 0, 9, 2, 7, 3 };
int size = sizeof(arr) / sizeof(arr[0]);
int i = 0;
my_qsort(arr, size, sizeof(int), int_cmp);
for (i = 0; i < size; i++)
{
printf("%d\n", arr[i]);//9876543210
}
system("pause");
return 0;
}
通过上面模拟实现我们了解到qsort能够进行排序,接下来我们来练习使用qsort函数排序各种类型的数据。
1.int 类型:
int int_cmp(const void *p1, const void *p2)
{
int *_p1 = (int *)p1;
int *_p2 = (int *)p2;
if (*_p1 < *_p2)
{
return 1;//降序
}
else if (*_p1 > *_p2)
{
return -1;
}
else
{
return 0;
}
}
int main()
{
int arr[] = { 1, 5, 4, 6, 8, 0, 9, 2, 7, 3 };
int size = sizeof(arr) / sizeof(arr[0]);
int i = 0;
qsort(arr, size, sizeof(int), int_cmp);
for (i = 0; i < size; i++)
{
printf("%d\n", arr[i]);//9876543210
}
system("pause");
return 0;
}
//2.double类型:
int double_cmp(const void *p1, const void *p2)
{
double *_p1 = (double *)p1;
double *_p2 = (double *)p2;
if (*_p1 < *_p2)
{
return -1;//升序
}
else if (*_p1 > *_p2)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
double arr[] = { 10.3, 5.0, 4.345, 6.66, 8.9, 0.1, 9.4, 2.5, 7.35, 3.09 };
int size = sizeof(arr) / sizeof(arr[0]);
int i = 0;
qsort(arr, size, sizeof(double), double_cmp);
for (i = 0; i < size; i++)
{
printf("%f ", arr[i]);
}
//0.100000 2.500000 3.090000 4.345000 5.000000 6.660000 7.350000 8.900000 9.400000 10.300000 请按任意键继续. . .
system("pause");
return 0;
}
//3.排序字符char类型:
int char_cmp(const void *p1, const void *p2)
{
char *_p1 = (char *)p1;
char *_p2 = (char *)p2;
if (*_p1 < *_p2)
{
return -1;//升序
}
else if (*_p1 > *_p2)
{
return 1;
}
else
{
return 0;
}
}
int main()
{
char arr[] = { 'v','A','g','t','H','M','d' };
int size = sizeof(arr) / sizeof(arr[0]);
int i = 0;
qsort(arr, size, sizeof(char), char_cmp);
for (i = 0; i < size; i++)
{
printf("%c ", arr[i]);
}
//A H M d g t v 请按任意键继续. . .
system("pause");
return 0;
}
注意字符串传参问题: 因为char*是字符数组,后面跟的相当于字符数组名,而指向字符数组(字符串)的指针,那就再加一星号,char**就是指向字符数组的指针了(当然它也可以表示字符串数组)。函数定义时使用char **s1和char **s2作参数,函数内容不需要变。
使用这两个形参后,在函数体中,*s1表示指针s1指向的字符数组(字符串),*s2表示指针s2指向的字符数组(字符串),因此,函数体不需要做改变。