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);
}
}
}
}
好啦,本章对于指针的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!
如你喜欢,点点赞就是对我的支持,感谢感谢!!!