目录
一,qsort函数的介绍
void qsort(void* base, size_t num, size_t width, int(*cmp)(const void* e1, const void* e2))
首先它是一个排序函数!能量巨大,可以排序任意类型的数据,整型,数组,结构体,字符等
qsort函数一共有四个参数:
1,void* base:是要排序的数据的起始位置,这是一个指针,返回类型为空的,void类型,必须是void*,不能擅作主张修改,改的是base,起始地址,用自己想用的。
2,size_t num:是待排序的数据元素的个数,传过去一个整型
3,size_t width:是待排序的数据元素的大小,单位是字节。传过去一个整型
4,int(*cmp)(const void* e1, const void* e2)这是函数指针,比较函数,可以自己去设计,具体怎么比较,看题目的具体安排。它的规定是,e1<e2 返回<0,e1=e2返回=0,e1>e2返回>0。
这样才能比较,这是这个函数的自有规定,按照这个规定来设计就行。注意e1是你要比较的元素的一个地址,e2是另一个元素的地址。
二,实例,用qsort函数,实现冒泡排序,把降序改为升序
1,代码逻辑简单,重点是cmp1函数
解析:两个元素地址传过来,是void*类型的,根本不可以进行加减操作,一定要把它强制类型转换为int*类型的,才可以进行加减操作。
这样,两个整型元素相减,返回值要么是>0,要么是=0,要么是<0,符合函数的规定。
然后,按照相减之后的值排序,例如 9-5=4 9-3=6,直接推出,9比5大。而结果的6比4大,间接推出5又比3大,所以,3,5,9排序成功,9在最后面,依次类推,遍历了各种情况,排序为由小到大的升序。
想要把升序,改为降序也可以,把return 的p1和p2调换即可。我想不用过多演示了吧。
三,用qsort函数,排序结构体变量
大致流程和上述例子一样,我把解析部分写在注释里面了,核心点是指针的强制类型转换,把void*类型转换为结构体指针类型,进行运算比较。
四,自己实现一个qsort函数,用冒泡思维写
1,分析判断两个数据是否需要交换
4个参数是固定的,循环的趟数是固定的,需要修改的是,判断两个数据,是否需要交换,以及怎么交换。
cmp函数和上述的一样,为什么if语句里面是,强制类型转换为char*呢?
因为在字节这个单位里面,char类型的最小,是一个字节,把整型4个字节强制类型转换为一个字节,然后地址加上宽度*j,就相当于跨过了一个整型,到了下一个整型的地址。
这绝对不是多此一举,下次如果是浮点型,结构体类型,就跨越 宽度*j,这种写法是通用方式。以此判断出两个数据的大小,到底是否需要交换。
精髓:要运算的是整型,但是转换为(char*)base+size*j,表示了一个整型地址,给cmp函数传过去,接收的是void*类型,无法加减,又得强制类型转换为int*类型。。。。。。。。。。。看到这要开骂了? 真不是画蛇添足。。
真得需要仔细琢磨,因为函数强大,以后可以用于各种情况,所以每一步的操作就得考虑其余情况的使用,这是通用版本。不能想当然的,就都使用整型指针不是最方便?那样这题是方便了,下次换个类型就一点没用了。。。。。。。
return 返回正数,说明前面的数大,需要交换。同理可知。
2,如何交换,
传参,传过来两个数据的地址,和宽度大小size,具体多少个字节。循环几次由字节数控制。强制类型转换为char*是一个字节,整型是4个字节,两个数据交换就需要4次,这是一个字节一个字节是交换,4次之后,就是整体交换完毕了。
看图吧,1个整型四个字节,1和1交换,2和2交换,3和3交换,4和4交换。这样具体一个整型就交换完毕了,以后要是浮点型,结构体,也可以这样,无非是循环多一点。
((char*)p1 + i)就是第一个数据的起始地址,((char*)p2 + i)是第二个数据的起始地址。i 每走一步,就是挪一个字节。
五,自己实现的qsort函数,的整体使用
整体没毛病,就是有点绕的慌
要是想要修改,排降序,只需要改动cmp函数那里就ok了,结构体排序也可以,但要记住传参过去,要强制类型转换。