在C语言中,如何使用qsort函数?(详解与模拟实现)

如何使用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 原型

qsort原型
查看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;
}

运行结果:
程序运行结果1

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;
}

运行结果:
程序运行结果2

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;
}

验证1
监视窗口可以看出已经对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;
}

结果2

总结

在以后写C语言代码时涉及到排序的,可以很方便的使用qsort函数,只需要编写一个简单的比较函数就能实现。(记住包含<stdlib.h>头文件)
通过自己模拟实现qsort函数,有两个比较重要的思想:

  1. 在对不确定类型的数据进行操作时,可以考虑将泛型指针强制类型转换为char*指针,以便于逐个字节对数据进行操作;
  2. 在将操作封装成函数时,如果某一部分的逻辑相同但实现方法各不相同,可以将这部分代码抽离出函数,写入参数部分,使用函数指针让使用者自己编写相对应的代码,以便于函数完成某一操作。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值