介绍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指向的字符数组(字符串),因此,函数体不需要做改变。