在书写之前,我先给大家讲解一下qsort()函数应该如何使用:
首先看到qsort()函数的书写:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
第一个参数为void* base:表示为数组的起始位置地址,用无类型指针接受。
第二个参数size_t num:我们可以简单的理解为int num,表示传入的数组内的元素个数有多少个。
第三个参数size_t width:我们可以理解为每个元素所占有的字节大小。
第四个参数int (__cdecl *compare )(const void *elem1, const void *elem2 ):表示为一个函数指针,表明这是一个函数的地址,这个函数传入的参数有两个,都是常量无类型数据地址,其中该函数是由我们人为书写的,返回类型为int,值为-1、0、1,代表元素1与元素2的大小比较值。这种在函数内部写入另一个函数地址的值被称为回调函数。不理解可以去学一学。
知道了这些,我们利用起来就很轻松了,这里我拿结构体的排序作为例子:
定义结构体:
//结构体
struct Stu
{
char name[20];
int age;
};
书写结构体内部年龄比较排序函数作为回调函数:
int cmp_Stu_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
我们从qsort的传参很轻松的知道,最后传入的函数指针的函数的传入参数为两个常量无类型数据地址,返回类型需要时整型,所以函数为整型,因为void*这种类型的指针不能加减和*解引用操作,那么在使用之前就需要提前强制转换类型为结构体的类型指针(struct Stu*)然后->指向年龄这一个参数。因为年龄为整型,所以直接返回元素1与元素2的差值就行。
书写qsort()函数传参函数:
void test2()
{
//结构体输入元素
struct Stu s[3] = {
{ "zhangsan", 20 },
{ "wangwu", 30 },
{ "lisi", 10 }
};
//求个数
int sz = sizeof(s) / sizeof(s[0]);
//求字节宽度
int width = sizeof(s[0]);
qsort(s, sz, width, cmp_Stu_age);
//输出改变的数组
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", s[i].age);
}
}
其中输入的s为结构体数组的首元素地址,sz为元素个数,width为结构体字节长度,cmp_Stu_age为回调函数名。然后写一个主函数调用test2()函数就完成排序了。
结果: (本来是20、30、10的循序,现在变为了10、20、30了)这样我们就成功调用了。
接下来就开始我们的自定义函数的书写啦!
我相信大家看到这篇文章都是知道对整型数组冒泡排序的代码是如何书写的,那么我就只列出这一段程序让你们回忆一下:(我相信其中应该不需要我讲解了,就没给注释)
void Bubble_Sort(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
int temp = arr[j];
if (arr[j]>arr[j + 1])
{
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_Sort(arr, sz);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
我们自定义的函数也是如同这种冒泡方式排序,只不过我们能排序的不只是整型。
接下来直接写下函数:
void Bubble_Sort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{
//第i个元素排序
int i = 0;
for (i = 0; i < num - 1; i++)
{
//冒泡
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
//将void*类型的数组或结构体转换为char*的地址,通过增加j*width找到下一个元素的地址
if (cmp((char*)base + j*width, (char*)base + (j+1)*width)>0)
{
//交换两个元素内的值,以及每个元素的字节数,当为结构体时,字节数为整个结构体大小
Swap((char*)base + j*width, (char*)base + (j+1)*width, width);
}
}
}
}
因为我们作为开发者,我们不知道使用者会使用何种类型的数据排序,那么这个时候,我们接收首元素地址的类型应该为无类型地址,但是作为使用者他一定知道他要排序的元素字节大小,所以我们再定义接收元素个数,字节长度,以及回调函数,这里和qsort()函数相同,不清楚可以翻一下前面的讲解。
中间的循环我就不解释,重点在if的条件判断,因为我们知道无类型地址不能加减,*解引用,也知道我们不知道使用者传入的元素的字节长度,那么我们就可以想到单个字节加上width大小的字节数,就能找到下一个元素的地址,所以书写方式为(char*)base+j*width,又因为我们知道回调函数再返回值为1时,就交换两个元素的值,所以写下这一句条件判断:
if (cmp((char*)base + j*width, (char*)base + (j+1)*width)>0)
当条件判断成功时,就会调用我们的Swap()交换函数,其中的思想为一个字节一个字节的交换,直到width次循环时,结束交换,这样就实现了我们的交换功能:
void Swap(char* ele1, char*ele2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
//每次交换一个字节的值
char temp = *ele1;
*ele1 = *ele2;
*ele2 = temp;
ele1++;
ele2++;
}
}
一下就是该函数的使用了,方法如同qsort()函数一样,我就不在重复讲解了。
//整型排序
void test1()
{
int arr[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
Bubble_Sort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
//结构体排序
struct Stu
{
char name[20];
int age;
};
void test2()
{
struct Stu s[3] = {
{ "zhangsan", 20 },
{ "wangwu", 30 },
{ "lisi", 10 }
};
int sz = sizeof(s) / sizeof(s[0]);
int width = sizeof(s[0]);
Bubble_Sort(s, sz, width, cmp_Stu_age);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", s[i].age);
}
}
//整型回调函数判断条件
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}
//结构体回调函数判断条件
int cmp_Stu_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
结果:
以上就是全部的代码了,希望能够帮到你,因为本人也没学多久,中途有错望多多包涵,谢谢!