本篇是用冒泡排序的方法来实现qsort函数(排序函数)的功能
要写一个自己的qsort函数,我们首先要知道qsort函数的功能与使用方法
由上面的介绍我们可以知道,qsort函数的作用是用来对数组里的元素进行排序,一共有4个参数,分别是他们的作用是先把数组的地址以void*的形式传过去,然后再传入这个数组的元素个数与每个元素的大小(单位字节),最后的参数是一个函数指针,这个函数指针指向的函数的作用是要告诉程序我们数组中的元素要如何进行比较,有两个参数,p1与p2,如果p1
我们首先以排序一个结构体为例,我们创建一个结构体类型struct S,成员有名字和年龄,我们以年龄为标准对三个结构体变量进行排序,我们先放出代码
struct S //创建结构体类型
{
char name[20];
int age;
};
int cmp_age(const void* e1, const void* e2) //写出比较函数
{
return (((struct S*)e1)->age - ((struct S*)e2)->age);
}
void Swap(void* df1, void* df2,int width) //交换函数,在进行排序时如果两个元素符合交换条件,则使用该函数对两个元素进行交换
{
int i;
for (i = 0;i < width;i++)
{
char tmp = *((char*)df1+i);
*((char*)df1+i) = *((char*)df2+i);
*((char*)df2+i) = tmp;
}
}
void my_bubblesort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{ //使用冒泡排序的方法对数组的元素进行排序
int i;
for (i = 0;i < num - 1;i++)
{
int j;
for (j = 0;j < num-1-i;j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width)>0)
{
Swap((char*)base + j * width,(char*)base + (j + 1) * width,width);
}
}
}
}
int main()
{
int i;
struct S a[3] = {{"zhangsan",28},{"lisi",21},{"wangwu",25}};
//struct S* p = &a[0];
int sz=sizeof(a)/sizeof(a[0]);
int width = sizeof(a[0]);
/*my_bubblesort(a,sz,width,cmp_age);*/
my_bubblesort(a, sz, width, cmp_age);
for (i = 0; i < 3;i++)
{
printf("%d\n", (a[i]).age);
}
return 0;
}
这个代码分为五个部分,首先,我们要先创建一个结构体类型,我创建的是struct S,然后在主函数中我们创建了一个元素类型为结构体的数组,假设该数组为a,有三个元素,如代码。现在我们要以年龄为标准,对a数组的三个元素进行排序,我们要以qsort的方式来创建一个my_bubblesort函数,那么我们自建的函数的参数和返回类型就也与qsort相同,也为4个参数如qsort,返回值为void。然后我们就可以着手用冒泡排序的方法实现对数组元素的排序。
冒泡排序,我们首先要知道冒泡排序的过程,我们先用一个数组的第一个元素与第二个元素进行比较,如果第一个元素大于第二个元素,那么我们就把这两个元素交换位置,然后比较第二个元素与第三个元素,以此往复,一直比较到倒数第二个元素与倒数第一个元素,完成这一趟,就是完成了一次冒泡排序,而我们要完成冒泡排序的次数应该是元素个数减一个,我们就可以使用一个for循环来完成这个目的,在我们进行一次冒泡排序时,我们还要使数组中的相邻两个元素进行比较,那我们就可以再用一个for循环来实现,而这时的循环次数就应该是num-1-i,因为之前的i次排序已经把后面的i个数排好了,所以就不用再循环num-1次了。而当我们每次两个元素进行比较时,我们就需要用到我们函数的第四个参数:比较函数了。
为什么要使用比较函数呢,因为数组里的元素可能是各种类型的,而每种类型的比较方法都不一样,比如我们结构体的比较方法和整型的比较方法就不同,而就算都是结构体,以上图为例,我们也可以用age为标准或者以name为标准进行比较,所以我们就无法写一个统一的函数来实现这个比较的功能,所以我们需要函数的使用者自己写一个比较函数作为参数,再函数调用时告诉函数我们进行排序的数组两个元素之间应该如何进行比较。
我们是以结构体中的age为标准进行比较的,所以我们就要再写一个函数,来实现两个结构体类型的元素如何以age为标准进行比较,我写的是cmp_age,参数为const void* e1与const void* e2,我们使用void*类型的意义是这个函数可以接收任何类型的指针作为参数,但是我们不能解引用他,在前面加上const的意义是使我们无法通过指针改变这个指针指向的内容,因为我们只是要对这两个元素进行比较,并不希望它指向的内容被改变,所以加上const会使我们的指针更加安全。而我们要使用这两个指针,首先就要对他们进行强制类型转换,因为我比较的是以年龄为标准的结构体变量,所以我就把e1和e2强制类型转换成struct S*,然后找到结构体变量中的成员age,然后对两个元素进行比较,如果e1>e2,返回一个大于0的数,相等返回0,e1 当我们完成比较后,发现我们正在比较的元素符合交换的条件时,我们就要对这两个元素进行交换,这时我们可以再写一个交换函数,因为我们同样不知道我们要进行交换的元素是什么类型的,所以我们同样使用void*作为参数来接收这来接收这两个元素的地址,然后我们还需要一个参数就是这个元素的宽度,而交换函数如何实现呢,首先,因为我们不知道元素的类型,那么我们如果要实现元素的交换,我们就可以把两个元素逐字节的进行交换,这时候就用到了元素的宽度,当我们逐个元素进行交换时,一直要交换到一个元素宽度,这时表示我们已经完全的交换了一个元素。 完成以上内容,我们就基本写完了my_bubblesort函数的内容,下面我们来使用一下看看效果。 下面我们再写一个例子,这次我们以name为标准进行比较,同样,我们先放代码 我们以name为标准进行比较时,需要注意的是name是一个元素类型为字符的数组,也是一个字符串,而字符串之间的比较需要用到一个库函数strcmp,头文件为 我们发现这就实现了以name为标准对数组的元素进行排序,我们发现,当我们想要排序不同元素类型的数组时,我们只需要写一个比较函数告诉程序我们要如何对两个元素进行比较,然后把这个比较函数作为参数传给排序函数,就可以实现排序 同样,我们也可以练习一下排序别的元素类型的数组来熟悉一下实现数组内容排序函数的过程,我们就不写了,大家可以自己练习一下。 如果我的文章对你有帮助,希望多多点赞,关注!! struct S
{
char name[20];
int age;
};
int cmp_name(const void* e1, const void* e2)
{
return strcmp((((struct S*)e1)->name),(((struct S*)e2)->name));
}
void Swap(void*bf1,void* bf2,int width)
{
int i;
for (i = 0;i < width;i++)
{
char tmp = *((char*)bf1 + i);
*((char*)bf1 + i) = *((char*)bf2 + i);
*((char*)bf2 + i) = tmp;
}
}
void my_bubblesort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{
int i;
for (i = 0;i < num - 1;i++)
{
int j;
for (j = 0;j < num - 1 - i;j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
int i;
struct S a[3] = { {"zhangsan",28},{"lisi",21},{"wangwu",25} };
//struct S* p = &a[0];
int sz = sizeof(a) / sizeof(a[0]);
int width = sizeof(a[0]);
/*my_bubblesort(a,sz,width,cmp_age);*/
my_bubblesort(a, sz, width, cmp_name);
for (i = 0; i < 3;i++)
{
printf("%s\n", (a[i]).name);
}
return 0;
}