如何使用qsort函数?(C语言)
- 1. 序言
- 2. 原型解析
- 2.1 原型
- 2.2 参数列表
- 3. 使用举例
- 3.1 对整型数据(int)进行排序
- 3.2 对结构体数据(struct)进行排序
- 4. 模拟实现
- 4.1 模拟实现
- 4.2 验证
- 总结
1. 序言
在C语言中,排序的方式有很多种,最开始我们接触的有冒泡排序,选择排序。其实,在C语言的库中有这么一种排序函数qsort,可以完成对各种类型数据的排序,放在<stdlib.h>的头文件中,底层使用的是快速排序(quick sort)。下面就来深度介绍qsort的原型和使用方法。
2. 原型解析
2.1 原型
查看cplusplus中对qsort函数的描述,可以知道qsort函数可以实现对任意类型的数据进行排序。
2.2 参数列表
qsort函数具有四个参数:
void* base: 指的是一个泛型指针指向待排序数组的起始地址
size_t num:指的是待排序元素的个数
size_t size:指的是每个元素的大小,单位为字节(byte)
int (*compare)(const void * , const void *):指的是一个函数指针,指向的函数需要使用者自己编写,目的是给出待排序元素两两之间的比较方法
在这四个参数中,看上去最麻烦的就是第四个函数指针的参数,下面再次对比较函数的原型进行理解
//比较函数
int compare (const void* p1, const void* p2)//两个参数均为指向待排序元素的泛型指针
{
return *(Mytype*)p1 - *(Mytype*)p2;
}
//当a指向的元素大的时候,返回大于0的数字
//当a,b指向的元素相等时,返回0
//当a指向的元素小的时候,返回小于0的数字
因为使用者在使用时是知道自己要排序的元素是什么类型的,所以在提供比较函数的时候需要将泛型指针(void*)强制类型转换对应的类型,然后进行比较
3. 使用举例
由于qsort对任意类型数据都可以进行排序,在此举出两个例子分别代表对已有类型和用户自定义类型进行排序
3.1 对整型数据(int)进行排序
#include <stdio.h>
#include <stdlib.h>//包含qsort相应头文件
int CmpByInt (const void* p1, const void* p2)//编写元素比较函数
{
return *(int*)p1 - *(int*)p2;
}
//注意。在写比较函数的时候参数类型和返回类型应与qsort第四个函数指针需要的参数保持一致
int main()
{
int arr[] = {5,6,4,2,8,9,7,1,0,3};//创建一个整型数组
size_t sz = sizeof(arr) / sizeof(arr[0]);//待排序元素个数
qsort(arr, sz, sizeof(arr[0]), CmpByInt);
int i = 0;
for(i = 0; i<sz; i++)
{
printf("%d ", arr[i]);
}//将排好序的数组打印到屏幕上
return 0;
}
运行结果:
3.2 对结构体数据(struct)进行排序
对结构体数据进行排序的时候,我们需要指定到底是按照结构体的哪一个成员进行比较,这样才有意义。
#include <stdio.h>
#include <stdlib.h>//包含qsort相应头文件
#include <string.h>
typedef struct Stu//创建结构体变量
{
char name[16];
int age;
}Stu;
//比较函数
int CmpByStu_name(const void* p1, const void* p2)//按姓名成员排序
{
return strcmp(((Stu*)p1)->name, ((Stu*)p2)->name);//需要包含<string.h>头文件
//在比较两个字符的时候使用strcmp函数,返回值恰好与要求相符
}
int CmpByStu_age(const void* p1, const void* p2)//按年龄成员排序
{
return ((Stu*)p1)->age - ((Stu*)p2)->age;
}
//打印函数
void print_Stu(Stu* Stu_arr, size_t sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d\n", Stu_arr[i].name, Stu_arr[i].age);
}
printf("\n");
}
int main()
{
Stu Stu_arr[] = { {"zhangsan", 18}, {"lisi", 23}, {"wangwu", 20}};//创建结构体数组
size_t sz = sizeof(Stu_arr) / sizeof(Stu_arr[0]);//计算结构体元素个数
qsort(Stu_arr, sz, sizeof(Stu_arr[0]), CmpByStu_name);
print_Stu(Stu_arr, sz);
qsort(Stu_arr, sz, sizeof(Stu_arr[0]), CmpByStu_age);
print_Stu(Stu_arr, sz);
return 0;
}
运行结果:
4. 模拟实现
由于快速排序底层算法相对复杂,在此使用相对较为简单的冒泡排序作为模拟实现的底层算法。
4.1 模拟实现
//使用冒泡排序模拟实现qsort函数
void swap(char* buf1, char* buf2, size_t width)//交换函数
{
int i = 0;
for (i = 0; i < width; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
//因为不知道交换元素类型,所以以字节为单位来进行交换
}
void bubblesort(void* base, size_t num, size_t width, int (*compare)(const void*, const void*))//参数列表仿照qsort函数
{
int i = 0;//趟数
for (i = 0; i < num - 1; i++)
{
int flag = 0;
int j = 0;//次数
for (j = 0; j < num - 1 - i; j++)
{
//使用比较函数比较大小
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//这里利用char*指针加减规律找到各个元素
{
//如果符合条件,交换两元素
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
flag = 0;
}
}
if (flag)//如果一趟中都没有交换,已经排序完成,提前结束
break;
}
}
此时,已经基本实现对各种类型的数据进行排序了。
4.2 验证
- 对int类型数据(已有类型)
int cmp_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
int main()
{
int arr[] = { 1,5,9,6,4,2,7,3,8,0 };
size_t sz = sizeof(arr) / sizeof(arr[0]);
bubblesort(arr, sz, sizeof(arr[0]), cmp_int);
return 0;
}
监视窗口可以看出已经对int类型数据可以完成排序
- 对结构体类型数据(自定义类型)
typedef struct Stu
{
char name[16];
int age;
}Stu;
int CmpByStu_name(const void* a, const void* b)
{
return strcmp(((Stu*)a)->name, ((Stu*)b)->name);
}
int CmpByStu_age(const void* a, const void* b)
{
return ((Stu*)a)->age - ((Stu*)b)->age;
}
void print_Stu(Stu* Stu_arr, size_t sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s %d\n", Stu_arr[i].name, Stu_arr[i].age);
}
printf("\n");
}
int main()
{
Stu Stu_arr[] = { {"zhangsan", 18}, {"lisi", 23}, {"wangwu", 20}};
size_t sz = sizeof(Stu_arr) / sizeof(Stu_arr[0]);
bubblesort(Stu_arr, sz, sizeof(Stu_arr[0]), CmpByStu_name);
print_Stu(Stu_arr, sz);
bubblesort(Stu_arr, sz, sizeof(Stu_arr[0]), CmpByStu_age);
print_Stu(Stu_arr, sz);
return 0;
}
总结
在以后写C语言代码时涉及到排序的,可以很方便的使用qsort函数,只需要编写一个简单的比较函数就能实现。(记住包含<stdlib.h>头文件)
通过自己模拟实现qsort函数,有两个比较重要的思想:
- 在对不确定类型的数据进行操作时,可以考虑将泛型指针强制类型转换为char*指针,以便于逐个字节对数据进行操作;
- 在将操作封装成函数时,如果某一部分的逻辑相同但实现方法各不相同,可以将这部分代码抽离出函数,写入参数部分,使用函数指针让使用者自己编写相对应的代码,以便于函数完成某一操作。
完