深入理解指针4

hello,各位小伙伴,本篇文章跟大家一起继续深入学习指针,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !
如果本篇文章对你有帮助,还请各位点点赞!!!
在这里插入图片描述

前言

本篇将会带着小伙伴们一起通过模拟实现qsort来继续深入学习指针
GO!GO!GO!开始学习:

qsort的使用

qsort是一个用来排序的库函数,可以升序可以降序

void qsort(数组名,比较的元数个数,比较的元素类型大小,决定升序还是降序的函数);
//举个例子:整型数组arr

int cmp(const void* p1,const void* p2)
{
	return (*(int*)p1 - *(int)p2);//升序
}
void qsort(arr,sizeof(arr)/sizeof(arr[0],sizeof(int),cmp);

关于void*指针在深入理解指针1有讲解,忘记的小伙伴可以去复习复习
qsort函数的使用需要4个参数,其中有一个是函数:

**1. 第一个参数(`arr`):`数组名`(需要排序的数组,可以是字符数组、结构体....)
2. 第二个参数(`sizeof(arr)/sizeof(arr[0]`):`需要排序的元素个数`
3. 第三个参数(`sizeof(int)`):`元素的大小`(单位是字节)
4. 第四个参数(`cmp`):`函数`(决定升序还是降序)**

所以qsort的声明:

void qsort(void* base, size_t num, size_t size, 
	int (*cmp)(const void* p1, const void* p2));

其实int (*cmp)(const void* p1, const void* p2)是一个函数指针,我们需要调用这个函数来决定我们是升序排序还是降序排序,这个函数是需要我们自己写的。此函数需要两个参数,并且返回一个整型(int)
这两个参数就是数组里的元素的值

返回值>0:这两个参数需要交换
返回值<0:这两个参数不需要交换
返回值=0:这两个参数不需要交换

qsort函数可以将任何数据进行排序,包括结构体:

struct Stu //学⽣
{
 char name[20];//名字
 int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
 return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
 return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
 struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
 int sz = sizeof(s) / sizeof(s[0]);
 qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
 struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
 int sz = sizeof(s) / sizeof(s[0]);
 qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
 test2();
 test3();
 return 0;
}

使用冒泡排序模拟实现qsort函数

qsort函数使用的是快速排序,但是由于本人水平不高,所以将会用冒泡排序代替。冒泡排序相信大部分小伙伴都已经熟练掌握了,所以我就不在这里讲解冒泡排序了。模拟的函数名:my_qsort

使用冒泡排序模拟my_qsort框架

所以我们能够写出my_qsort大概框架:

void my_qsort(void *base, size_t count , size_t size, int(*cmp )(void *, void *))
{
	 int i = 0;
	 int j = 0;
	 for (i = 0; i< count - 1; i++)
 	{
 		for (j = 0; j<count-i-1; j++)
			 {
	 		//排序操作...
			 }
	 }
}

如何交换元素

那么该怎么进行排序?上文讲到:qsort函数可以将任何数据进行排序。所以我们要应对所有数据:int、char、结构体…

由于每种元素的大小都不一样,所以我们只能一个字节一个字节去交换,也就是说我们需要强制类型转换为char类型,举个例子:交换两个整型数据

void my_swap(void* a1,void* a2,size_t size)
{
	for(int i = 0;i < size;i++)
	{
		char tmp = *((char *)a1 + i);
 		*(( char *)a1 + i) = *((char *) a2 + i);
		*(( char *)a2 + i) = tmp;
	}
}

所以小伙伴们知道为什么要传递参数元素的大小了吧,就是为了知道每次交换元素需要交换多少个字节

决定升序排序还是降序排序

上文讲了int (*cmp)(const void* p1, const void* p2)这个函数指针,它的作用就是来决定升序还是降序的

int cmp(const void* p1,const void* p2)
{
	return (*(int*)p1 - *(int)p2);//升序
	//return (*(int*)p2 - *(int)p1);//降序
}

还讲到该函数返回值>0才进行交换元素
这就好理解了,前一个元素 - 后一个元素 > 0,升序(小的排在前面),
后一个元素 - 前一个元素 > 0,降序(大的排在后面)

但是由于我们不知道传过来的数据是什么类型,所以我们还是用void*来接收参数,然后强制类型转换为(int*),进行相减。由于我们不需要改变这两个参数,所以加了个const

“组装”my_qsort

所以my_qsort的零件就准备好啦,剩下就是组装了:

int cmp(const void* p1,const void* p2)
{
	return (*(int*)p1 - *(int)p2);//升序
	//return (*(int*)p2 - *(int)p1);//降序
}

void my_swap(void* a1,void* a2,size_t size)
{
	for(int i = 0;i < size;i++)
	{
		char tmp = *((char *)a1 + i);
 		*(( char *)a1 + i) = *((char *) a2 + i);
		*(( char *)a2 + i) = tmp;
	}
}

void my_qsort(void *base, size_t count , size_t size, int(*cmp )(void *, void *))
{
	 int i = 0;
	 int j = 0;
	 for (i = 0; i< count - 1; i++)
 	 {
 		for (j = 0; j<count-i-1; j++)
		{
	 		if(cmp(cmp ((char *) base + j*size , 
	 		(char *)base + (j + 1)*size) > 0))
	 		{
	 			my_swap(( char *)base + j*size,
	 			 (char *)base + (j + 1)*size, size);
	 		}
		 }
	  }
}

好啦,本章对于指针的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

​​​​​​​​​​​​​​

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值