qsort函数使用
再C语言stdlib.h库中,提供了一个用来排序的函数。
这个函数就是qsort函数,qsort函数的返回值是void类型,有四个参数。qsort函数排序完成后,默认是升序的。
void qsort(void*x, size_t num, size_t size, int (*P)(const void* p1, const void* p2));
第一个参数是排序的起始位置,第二个是需要排序的元素个数,第三个是元素的字节长度,第四个是用来判断大小的函数。
第四个参数是回调函数,这个函数是需要使用者自己提供的,也就是当我们使用qsort函数时,还需要自己写一个判断函数。设计这个函数时,函数的返回类型是int,两个参数都是void*类型的地址,在使用使用这两个地址时,需要进行强制类型转换,因为void*类型的是无法直接使用的,用const修饰是因为我们只是两个地址的两个元素,并不改变,使函数的安全性变高。
#include <stdio.h>
#include <stdlib.h>
int cam_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
int main()
{
int arr[] = { 5,2,6,4,7,9,4,1,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cam_int);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
在上面的cam_int函数内我们我们在使用两个地址时需要先一步进行强制类型转换。这样才能正常使用两个地址。
我们还可以用qsort函数来排序字符串
只需要重新写一个cam_char函数就行。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int cam_char(const void* p1, const void* p2)
{
return *(char*)p1 - *(char*)p2;
}
int main()
{
char arr[] = "dbaksfh";
qsort(arr, strlen(arr), sizeof(arr[0]), cam_char);
printf("%s", arr);
return 0;
}
也可以用来排序结构体。
#include <stdio.h>
#include <string.h>
#include<stdlib.h>
struct stu {
char name[20];
int age;
};
int cam_stu_name(const void* p1, const void* p2)
{
return strcmp((*(struct stu*)p1).name, (*(struct stu*)p2).name);
}
int cam_stu_age(const void* p1, const void* p2)
{
return (*(struct stu*)p1).age - (*(struct stu*)p2).age;
}
int main()
{
struct stu s[3] = { {"wang",18}, {"lishi",24}, {"zhag",14} };
qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), cam_stu_name);
for (int i = 0; i < sizeof(s) / sizeof(s[0]); i++)
{
printf("{%s %d} ",s[i].name,s[i].age);
}
printf("\n");
qsort(s, sizeof(s) / sizeof(s[0]), sizeof(s[0]), cam_stu_age);
for (int i = 0; i < sizeof(s) / sizeof(s[0]); i++)
{
printf("{%s %d} ", s[i].name, s[i].age);
}
return 0;
}
接下来我们更加深入了解qsort函数,qsort函数使用的排序方法使快排,我们则用冒泡排序来代替快排,实现一个冒泡排序的qsort函数。
冒泡排序
冒泡排序就是将第一个元素与第二个元素比较,当前面的元素小于后面的元素是时,就将它们两个交换,然后重复上面的操作,将第二个元素与第三个元素比较。当完成这样一次循环后,最后一个元素就是最大的那个。然后再次循环,这次要比较的元素就少一个,就是最后一个,因为最后一个已经排好了。每次循环都排好一个元素。
#include <stdio.h>
int main()
{
int arr[10] = { 3,4,7,8,4,2,1,5,9,8 };
int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10 - i - 1; j++)
{
if(arr[j]>arr[j+1])
{
int x = arr[j];
arr[j]=arr[j+1];
arr[j + 1] = x;
}
}
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
模拟qsort函数
上面我们实现了冒泡排序,接下来我们模拟实现qsort函数。
首先分析qsort函数组成。
void qsort(void*arr, size_t num, size_t size, int (*p)(const void*p1,const void* p2))。
就像上面说的一样,第一个参数设计成void*是为了接收所有类型的地址。
那现在我们需要知道第二和第三个参数有什么用。
其实可以推测出来,第二和第三个参数的作用就是将元素的地址传给第四个参数。
知道num我们就知道了要比较多少个元素,知道了size我们就知道每个元素的地址之间相差几个字节。
当我们想寻找第二个元素是就可以通过(char*)arr+size这个地址来访问。这里要记得强制类型转换char*,因为void*不能直接使用并且char*类型就是一个字节,加size就是跳过几个字节。
#include <stdio.h>
bubble_sort(void*arr,size_t num,size_t size,int (*p)(const void*p1,const void*p2))
{
int i = 0;
int j = 0;
for (i = 0; i < num; i++)
{
for (j = 0; j < 10 - i - 1; j++)
{
int x = (*p)((char*)arr + j * size, (char*)arr + (j + 1) * size);
if (x > 0)
{
char w = *((char*)arr + j * size);
*((char*)arr + j * size) = *((char*)arr + (j + 1) * size);
*((char*)arr + (j + 1) * size) = w;
}
}
}
}
int cam_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
int main()
{
int arr[10] = { 4,7,2,5,6,9,0,1,4,3 };
bubble_sort(arr, 19, sizeof(arr[0]), cam_int);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
这样一个使用冒泡排序写的qsort函数就完成了