目录
一、前言
我们学习过的冒泡排序,插入排序,选择排序等经典排序方法,为我们数据排序提供了稳定思路,但局限在于排序数据类型的单一。为了解决这一问题,c语言中提供了库函数qsort解决。我们今天就通过自定义函数实现qsort的功能,排序方法采用基本的冒泡排序
二、从MSDN认识qsort
1.分析:
我们需要传入四个变量:base——待排序数组的首元素地址;
num——数组内的元素个数;
width——每个元素的大小(单位为字节);
函数指针——传入两个数的地址,比较二者大小(需自己设计)
2.void*的特殊性
1.可以传入任何类型的指针而不会报警告,适合我们实现任何数据类型排序的目的;
2.不可以对其进行解引用操作,除非通过强制类型转换()确定其具体类型;
3.不可以对其进行加减运算,原因是不知道类型所以不知道步长,因此还是需要强制类型转换
三、cmp_函数的实现
注意:都需要先进行强制类型转换才可以解引用
1.比较int类型数据
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
2.比较char类型数据
char类型数据不可以用<>=进行比较,所以要用函数strcmp库函数,头文件为#include<string>
int cmp_char(const void*e1, const void*e2)
{
return strcmp((char*)e1, (char*)e2);
}
3.比较float类型函数
因为返回的是int类型,所以我们加入了判断语句。用数的正负表示数的大小情况
int cmp_float(const void*e1, const void*e2)
{
if ((float*)e1 > (float*)e2)
return 1;
else if ((float*)e1 ==(float*)e2)
return 0;
else
return -1;
}
4.比较结构体类型函数
结构体数组元素不可以直接比较,必须先选出结构体某一内容进行比较
typedef struct book{
char name[20];
int prince;
}b;//重命名struct book为b
int cmp_stru_price(const void*e1, const void*e2)
{
return ((b*)e1)->prince - ((b*)e2)->prince;
}
注意!!!(b*)e1外面的括号不可以省略,因为->的优先级比强制类型转换高
四、bubble_sort函数的实现
首先思考一个问题:既然传入的指针为void*类型,如何实现步长的确定?聪明的科学家想到利用width作为我们的标准
void bubble_sort(void*base,int sz,int width,int(*cmp)(void*,void*))
{
int i; int j;
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1-i; j++)
{
if (cmp((char*)base + width*j, (char*)base + width*(j + 1))>0)
{
swap((char*)base + width*j, (char*)base + width*(j + 1),width);
}
}
}
}
分析:
1.int(*)(void*,void*)是一个函数指针,指向我们之前设计的大小比较函数
2.在使用base的时候要先强制类型转换才可以作加减运算
3.漂亮的地方在于,不管实际传入的base是什么类型,我们都将其转化为char*类型的指针,因为char*的步长最小为1,加上width就可以准确指向下一个
以int为例,width为4,所及就可以指向下一个元素
五、swap函数的实现
void swap(char*buff1,char*buff2,int width)
{
int i;
for (i = 0; i < width; i++)
{
int tmp = *buff1;
*buff1 = *buff2;
*buff2 = tmp;
buff1++;
buff2++;
}
}
值得关注的是,交换不是一次就可以了,因为我们现在是char*类型的指针,如上图所画,一次只能交换一格,如交换int的,要交换4次
六、重难点总结
1.函数指针的使用,使我们每次只需导入大小比较函数的地址即可,而不必写多个函数,分别含有不同的大小比较函数,减少了重复代码的出现
2.width的使用相当于告诉了我们进行比较的数组元素的类型,满足我们实现各种数据类型排序的需求
3.void*函数海纳百川,方便了我们的输入,只是注意在使用时的强制类型转化,否则无法使用
希望上述的总结对大家的理解有帮助,也更好的欣赏回调函数(这里为用函数指针调用函数)的魅力。