qsort是一个排序函数,它可以排序整形,字符型甚至是结构体,下面让我们看看qsort函数是如何实现不同类型的排序
对于qsort,返回的类型是void类型,传递的形参有四个,分别是待排序数组的第一个对象首元素的地址;数组中元素的个数;每个元素的大小;以及一个比较函数。
下面先让我们来看qsort中形参的比较函数:
在比较函数中,无论是整型还是字符型元素,在计算机中都是以ASCII码的形式进行存储与计算,那么比较函数的返回值类型应该是int 类型。
而对于形参,我们应当遵循int cmp(const void*e1,const void*e2)的形式。因为void*类型的指针可以接收任何类型的指针。
假如现在用qsort排序一个整型字符,那么比较函数中的形参接收到的就是两个整型的指针,在返回值中必须将void类型的指针强制转换成int类型的指针:
int arr_sub(const void*e1,const void*e2) {
return (*(int*)e1 - *(int*)e2);
}
对于qsort排序一个字符型数组,那么在返回值中需要将void类型指针强制转换成char类型再进行比较,刚好在库函数中有一个strcmp函数,就是用来比较字符大小的,我们可以利用它:
int arr_sub(const void*e1,const void*e2) {
return strcmp((char*)e1, (char*)e2);
}
最后对于结构体的比较函数,就显得复杂一点,需要根据结构体成员的类型来进行确定比较函数的返回值:
struct stu
{
char name[20];
int age;
int num;
};
//比较名字
int cmp_name(const void* e1, const void* e2) {
return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);//利用库函数进行比较
}
//比较年龄
int cmp_age(const void* e1,const void* e2) {
return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
//比较学号
int cmp_num(const void* e1, const void* e2) {
return ((struct stu*)e1)->num - ((struct stu*)e2)->num;
}
以下是利用qsort来实现冒泡排序:
int arr_sub(const void*e1,const void*e2) {
return strcmp((char*)e1, (char*)e2);
}
int main() {
int arr[] = { 'a','e','d','g','k','b'};
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(int), arr_sub);
for (int i = sz-1; i >=0 ; i--) {
printf("%c\n", arr[i]);
}
}
结果如下:
按照qsort的实现逻辑,我们也可以自己来进行模拟实现。
模拟实现中的比较函数与库函数中的交换函数一致,只需要注意此时排序的是什么类型的数组。
在交换函数中,我们不确定待排序的数组是什么类型,所以我们直接通过字节来交换,因为数组中元素类型一致,通过强制转换成char类型再解引用进行字节的交换。
最后是最主要的逻辑:冒泡排序
#include<stdio.h>
#include<assert.h>
//qsort函数形参:地址,元素个数,每个元素的字节数的大小,比较函数
int cmp(const void* e1, const void* e2) {
return *(int*)e1 - *(int*)e2;
}
//交换函数(两个元素之间的交换)
//一个字节一个字节的交换,直到交换的字节数等于一个元素的字节数
void swap(void*p1,void*p2,int size) {//指向两个字节,每个元素的大小
for (int i = 0; i < size; i++) {
/*char temp = *(char*)p1;
*(char*) p2 =*(char*)p1;
*(char*)p1 = temp;*/
/*p1 = (char*)p1 + 1;
p2 = (char*)p2 + 1;*/
char temp = *((char*)p1 + i);
*((char*)p1 + i)= *((char*)p2 + i);
*((char*)p2 + i)=temp;
}
}
//通过冒泡排序的样式对每个字节都进行交换,因为不确定元素的类型
void my_qsort(int*arr,int sz,int size,int(*cmp)(const void*,const void*)) {
for (int i = 0; i < sz-1; i++) {
for (int j = 0; j < sz - i - 1; j++) {
// j * size与(j + 1) * size跳过一个元素
//两个元素的第一个字节就可以判断出这两个元素的大小
if (cmp((char*)arr + j * size, (char*)arr + (j + 1) * size) > 0) {
swap((char*)arr + j * size, (char*)arr + (j + 1) * size,size);
}
}
}
}
int main() {
int arr[] = { 1,3,2,8,5,4,7,0,9,65 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr,sz,sizeof(int), cmp);
for (int i = 0; i < sz; i++) {
printf("%d", arr[i]);
printf("\n");
}
}