1,认识qsort函数
void qsort(void*base,
size_t num,
size_t width,
int(*cmp)(const void* e1)(const void* e2))
包含四个参数,一个参数是你要排序的数组,可以是结构体数组,可以是整形数组,也可以是字符串数组。第二个参数是数组元素个数,第三个参数是一个元素所占内存空间大小,第四个参数是一个函数,用到了函数指针来接收,这个函数要具备这些功能,它的返回值是正数,0或者负数,当是正数的时候我们就交换,这很容易理解。
我把整数交换的cmp函数写以下,便于理解。
int cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
这里我们要知道一个知识点 void*指针是不能直接解引用的,也不能加减整数的操作要先进行强制类型转换,所以我用了int*将其强制类型转换后再解引用,当e1大于e2时,返回正数就进行交换,这里是顺序排列,我们可以把e1和e2的顺序交换一下,就可以进行逆序排列。
到这里我们就初步认识了qsort函数,接下来我们要来复现它。
2.qsort的函数复现
我们命名一个函数就叫sort吧(字少,嗯)
这是主函数,目的是得到我们需要的参数
int main()
{
//结构体数组这个暂时不看,我们先看下面的整数数组
struct peo a[3] = { {"zhangsan",30},{"lisi",25},{"wangwu",40}};
//定义一个整数数组
int arr[10] = {9,8,7,6,5,4,3,2,1,0};
利用sizeof计算出元素个数得到我们的第二个参数
int sz = sizeof(arr) / sizeof(arr[0]);
sort(arr, sz, sizeof(a[0]),cmp);
//打印好排列的元素
for (int i = 0; i < sz; i++)
{
printf("%c \n", arr);
}
return 0;
}
1.实现sort函数
sort函数的主体框架实际上就是一个冒泡排序
不懂冒泡排序的可以看我上一篇文章里的排序实现的讲解
void sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < sz-1; i++)
{
int flag = 1;
for (int 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);
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
第一个要点
if语句里的条件表达是调用了我们开头讲的cmp函数,大于0就执行swap函数,否则不执行
第二个要点
因为我们要比较不同类型的数据,先转换成char*类型的指针适合所有类型,然后传到cmp里面转换成void*类型的指针,最后在return哪里转换成需要的类型。
第三个要点
这里我们加了一个flag判断结构,第一步令flag等于1,我们知道每个外循环只确定一个元素的位置,在下面的if语句中,通过比较两个元素的大小,如果在这次循环中没有进行交换,说明元素都
以位于正确的位置,可以提前结束循环。
2.实现swap函数
因为不能确定元素的类型,所以我们在交换的时候也使用char类型进行一个字节一个字节的交换
void swap(char* buf1, char* buf2, int width)
{
for (int i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
我们传进来的是个char类型指针(接收首元素的地址,如果是整形,相当于把整形拆分成四个字节进行平行交换,等价于交换整型元素)。
3.组合起来就成为了qsort函数
void swap(char* buf1, char* buf2, int width)
{
for (int i = 0; i < width; i++)
{
char temp = *buf1;
*buf1 = *buf2;
*buf2 = temp;
buf1++;
buf2++;
}
}
void sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{
for (int i = 0; i < sz-1; i++)
{
int flag = 1;
for (int 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);
flag = 0;
}
}
if (flag == 1)
{
break;
}
}
}
3.进行结构体元素的比较
1.创建一个结构体
struct peo
{
char name[20];
int age;
};
2.改主函数参数
int main()
{
//初始化结构体
struct peo a[3] = { {"zhangsan",30},{"lisi",25},{"wangwu",40}};
int arr[10] = {9,8,7,6,5,4,3,2,1,0};
int sz = sizeof(a) / sizeof(a[0]);
sort(a, sz, sizeof(a[0]),cmp_name);
for (int i = 0; i < sz; i++)
{
printf("%s \n", a[i].name);
}
return 0;
}
3.写cmp函数
这里用到了strcmp函数,记得要引用头文件
#include<string.h>
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct peo*)e1)->name,((struct peo*)e2)->name);
}
4.结束
说实话这一篇有点小饶, 重点是强制类型转换的使用和指针的运用,效果应该是差不多的。这篇内容到这里就结束了。