C语言中qsort库函数的简单应用与模拟实现

本文详细介绍了C语言中的冒泡排序算法,以及如何使用qsort函数进行整型和结构体数据的排序,重点讲解了void*指针、比较函数的编写和参数传递。作者通过示例展示了如何根据需要实现降序或升序排列,以及如何模拟qsort功能并自定义比较逻辑。
摘要由CSDN通过智能技术生成

一、冒泡排序

void sort(int arr[], int sz)
{
	//这是一个冒泡排序的函数
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}
}

这是一个常见的冒泡排序的函数。

仅仅只能对整型类数据进行排序。C语言库函数中提供了qsort函数,可以对任意类型的数据进行排序。

二、qsort函数的简单使用

qsort函数是C语言自带的库函数,在使用他之前,首先需要了解,需要给他传几个参数,以及这几个参数分别对应的类型是什么

https://zh.cppreference.com/w/c/algorithm/qsort

从这里可以看出,在使用qsort函数的时候,需要传四个参数进去。

1. 一个void*类型的指针,指向待排序数组的首元素

2.所求数组的元素个数

3.所求数组元素的大小

4.一个函数指针(指向用于比较数组元素的函数)

这里略微说一下自己的拙见

①.void*类型指针

因为qsort函数要实现可以对任意数据类型进行排序,所以这里用void*类型指针进行接收。

②int(*comp)(const void * p1,const void*p2)

指向一个数组元素对应数据类型的比较函数,返回值的规律为

return *p1,*p2比较
>0的值*p1>*p2
<0的值*p1<*p2
0*p1 == *p2

简单应用

Ⅰ、整型数组的排序

这是一段简单使用的代码,以排序一个整形数组为例。

在这里,前三个参数已经搞定,需要解决的就是第四个参数。


int arr[] = { 3,12,35,8,9,6,59,0,8, };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);

那么就来写一个比较int类型的函数

因为qsort函数参数中这个指针是用的这种形式,所以我们写的cmp_int 函数的形式为

int cmp_int(const void* p1, const void* p2)

这里需要注意

int cmp_int(const void* p1, const void* p2)
{
	if(*p1 > *p2)
}

不能这样写,因为传参类型是void*,不能直接解应用, 将指针类型强制转换成需要进行比较的指针类型进行比较

int cmp_int(const void* p1, const void* p2)
{
	
	if (*(int*)p1 > *(int*)p2)
		return 1;
	else if (*(int*)p1 == *(int*)p2)
		return 0;
	else if (*(int*)p1 < *(int*)p2)
		return -1;
}

这样写虽然可以,但是代码会显得比较冗余,可以进行简化。

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
	
}

直接返回两数做差的值,效果相同还更加简洁。

完整代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p2 - *(int*)p1;
}
int main()
{
	int arr[] = { 3,12,35,8,9,6,59,0,8, };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
		printf("%d ", arr[i]);
	return 0;
}
	

测试一下

可见,数组是按照升序排布,那如果想按照降序排的话,只需要将cmp_int函数中判断逻辑稍作更改即可

如下

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p2 - *(int*)p1;
}
int main()
{
	int arr[] = { 3,12,35,8,9,6,59,0,8, };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
		printf("%d ", arr[i]);
	return 0;
}

测试一下

Ⅱ、结构体的比较

接下来实现对结构体变量的比较

struct Stu
{
	char name[20];
	int age;
};

为了更加直观的看出比较的结果,在定义的结构体变量Stu中我们只定义age与name两个成员。

结构体变量进行比较时,使用结构体中的一成员进行比较,根据需求进行选择

int cmp_Stu_by_age(const void *p1,const void *p2)
{
    return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

int cmp_Stu_by_name(const void *p1,const void *p2)
{
    return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);  
}

用age判断的逻辑与cmp_int的逻辑相同,只是这里强制转换为结构体变量类型

用name判断则是使用到了strcmp这个字符串比较函数

整体代码如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{
	char name[20];
	int age;
};
int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int cmp_stu_by_age(const void* p1, const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
void test_stu_by_name()
{
	struct Stu arr[3] = { {"zhangsan",20},{"lisi",19},{"wangwu",35} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i].age);
	}//这里只是测试写的函数是否可用,以上帝视角进行观察,就只用把age打印出来就知道顺序有没有更换了
}
int main()
{
     test_stu_by_name();
     return 0;
}

测试一下

三、qsort函数的简单模拟实现

#include<stdio.h>
#include<stdlib.h>
void sort(int arr[], int sz)
{
	//这是一个冒泡排序的函数
	int i, j;
	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}
}

基于这个冒泡排序函数模拟qsort函数的功能

void bubble_sort(void *base,int sz,int width,int (*cmp)(const void *p1,const void *p2))

这里参考qsort的参数类型进行改造

因为要实现可以进行任何数据类型的比较,所以用void*指针接收数组的第一个元素
 要确定一个数组元素大小是多少,定义一个参数是width
 比较过程用一个函数指针接收一个函数,什么数据类型比较传对应的比较函数

这里我们对内部进行改造,比较趟数上没有什么大毛病,需要进行更改的地方就是比较过程与交换过程

void bubble_sort(void *base,int sz,int width,int (*cmp)(const void *p1,const void *p2))
{
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char *)base + j * width,(char *)base + (j + 1)* width)>0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}

这里因为使用void*类型接收的数据,所以不能确定一个元素到底有多大,需要手动传一个元素的大小过去,就是width

最关键的一点在于,如何给cmp这个比较函数传参

arr[j]  arr[j+1]
原来的比较是这样进行比较,但是在改造的过程中已经没有arr这个变量了,只剩下了base这个指针变量
那可以直接用base + j ,base + j + 1这种表示形式嘛?
很明显不行

base是个void*指针,不能直接进行计算,那么这时候,width就派上用场了,因为这里大小我们使用sizeof进行计算,单位是字节,那么我们就直接将base强制类型转换为char*类型,就可以直接跳过对应的宽度。

改写为(char*)base + j *width,(char*)base +(j + 1) * width。

对应的比较函数就不多做赘述了,与上文相似。

接下来就是交换数组中的元素顺序

这种交换方式比较局限,将交换过程写进一个函数Swap

void Swap(char *p1,char *p2,int wid)
{
	for (int i = 0; i < sz; i++)
	{
		char temp = *(p2 + i);
		*(p2 + i) = *(p1 + i);
		*(p1 + i) = temp;
	}
}

cmp中已经强制类型转换为char*类型,这里Swap的参数也采用char*类型

需要注意的是,这里只传起始地址是不够的,需要知晓到底要交换多少个字节,需要传入比较元素的大小

如图,如果不传入wid,那么以整型为例,整型大小为4个字节,只交换起始地址显然不会完全交换。

整体代码如下

#include<stdio.h>
#include<stdlib.h>
void sort(int arr[], int sz)
{
	
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}
}
void Swap(char *p1,char *p2,int wid)
{
	for (int i = 0; i < sz; i++)
	{
		char temp = *(p2 + i);
		*(p2 + i) = *(p1 + i);
		*(p1 + i) = temp;
	}
}

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
void bubble_sort(void *base,int sz,int width,int (*cmp)(const void *p1,const void *p2))
{
	
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char *)base + j * width,(char *)base + (j + 1)* width)>0)
			{
				
				Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
void test()
{
	int arr[] = { 3,12,35,8,9,6,59,0,8, };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	for (int i = 0; i < sz; i++)
		printf("%d ", arr[i]);
}
int main()
{
	test();
	return 0;
}

测试一下

再测试一下结构体

void Swap(char* p1, char* p2, int sz)
{
	
	for (int i = 0; i < sz; i++)
	{
		char temp = *(p2 + i);
		*(p2 + i) = *(p1 + i);
		*(p1 + i) = temp;
	}
}

void bubble_sort(void* base, int sz, int width, int (*cmp)(const void* p1, const void* p2))
{
	
	for (i = 0; i < sz; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
void sort(int arr[], int sz)
{
	//这是一个冒泡排序的函数
	int i, j;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}
}
struct Stu
{
	char name[20];
	int age;
};

int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void test_stu_by_name()
{
	struct Stu arr[3] = { {"zhangsan",20},{"lisi",19},{"wangwu",35} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i].age);
	}//这里只是测试写的函数是否可用,以上帝视角进行观察,就只用把age打印出来就知道顺序有没有更换了
}
int main()
{
	test_stu_by_name();
	return 0;
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值