本篇文章我们来了解一下回C语言中qsort函数的使用方法和模拟实现。这是一个通用性很强而且非常方便的库函数,通过这篇文章希望能让你了解sort函数。
目录
一、qsort的介绍:
二、qsort函数的使用
1.qsort排序整形
2.qsort排序字符串
3.qsort排序结构体中的整形
4.qsort排序结构体中的字符串
三、qsort的实现
qsort的使用需要引用头文件
然后我们来看qsort需要的参数,一共四个参数,具体要求如下:
①void* base——待排序目标的地址
②size_t num——表示的是待排序目标需要排序的个数,可以是整个要排序的目标,也可以是目标中的一段部分
③size_t——width,表示待排序数据的宽度
④表示一个函数指针,需要我们传入一个自定义的比较函数,用来比较两个数据中的大小关系。
代码如下:
//比较函数:
int compare(const void* r1, const void* r2)
{
return (*(int*)r1 - *(int*)r2);
}
int main ()
{
int arr[10] = { 1,3,5,7,6,2,0,8,9,4 };
qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(int), compare);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
这里有几个需要注意的点:
①compare函数,这个函数可以传入qsort函数中,让qsort函数知道两个数据间的大小关系,这个函数是需要我们自己创建的,我们只要书写了比较函数,我们可以比较绝大多数数据类型。
②compare函数中的参数我们必须写成const void*类型,因为void*型可以接受任意类型的数据,我们只需要传入width就可以控制操作的字节数,不会像int*、char*型将类型定死,这样就会导致qsort失去了通用性
③qsort中一个参数是一种函数指针类型,所以我们将函数的函数名传入就行了,因为函数名就表示函数地址。
④两个元素间的大小关系,如果是升序,即r1-r2,如果是降序,就是r2-r1。qsort默认的排序方式是升序,即r1-r2>0的情况
qsort函数的通用性关键就在于我们自己定义的比较函数,接下来我们写写看比较字符串、结构体里的整形/字符串如何使用qsort实现,以及对应的compare如何书写。
qsort函数最关键的其实就是我们书写的比较函数。我们来分析一下比较字符串函数的书写。
首先,这里我将arr1、arr2、arr3放入arr_name数组中,arr1、arr2、arr3表示首元素的地址,而arr_name也表示首元素的地址,所以arr_name是一个二级指针,我们将一些常规参数书写后开始书写comp_name函数,固定的函数参数const void*型。
其次,我们比较字符串的大小不能直接使用加减乘除,我们要使用strcmp函数来比较字符串。
然后,strcmp函数在比较完之后会返回一个值来表示两个字符串的大小关系,共有三种情况,str1>str2,会返回一个大于0的值;str1
最后,我们开始将r1指针进行强制类型转化,因为arr_name是一个二级指针,所以我们要先将r1、r2转化为char**型的二级指针,而strcmp函数比较的是一级指针,所以我们再对此指针解引用一次,就可以将对应arr1、arr2中字符串的首地址传入strcmp函数被正常调用。
代码如下:
//使用strcmp函数比较字符串
int comp_name(const void* r1, const void* r2)
{
return strcmp( *((char**)r1), *((char**)r2));
}
int main()
{
char arr1[20] = "zhangsan";
char arr2[20] = "lisi";
char arr3[20] = "wangwu";
char* arr_name[3] = { arr1,arr2,arr3 };
char** p = arr_name;
qsort(arr_name, sizeof(arr_name) / sizeof(arr_name[0]), sizeof(arr_name[0]), comp_name);
printf("%s\n", arr_name[0]); //李四第一
printf("%s\n", arr_name[1]); //王五第二
printf("%s\n",arr_name[2]); //张三第三
}
注意事项:
1.因为我传入的是结构体数组,所以我要将r1、r2强制类型转换为结构体指针,并且要保证结构体类型相同,都是struct stu类型的。
2.因为r1、r2是结构体指针了,我就可以直接使用r1来指向结构体中的数据,从而来比较结构体数据中的大小。
//比较函数:
int com_struct_score(const void* r1, const void* r2)
{
return ((struct stu*)r1)->score - ((struct stu*)r1)->score;
}
int main ()
{
//定义一个结构体数组
struct stu student_arr[3] = { { 210603,"wangwu",90 },{ 210602,"lisi",70 } ,{ 210601,"zhangsan",80 } };
//排序前
printf("排序前:\n%s %d\n", student_arr->name, student_arr->score);
printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);
printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);
//排序
qsort(student_arr, sizeof(student_arr) / sizeof(student_arr[0]), sizeof(student_arr[0]), com_struct_score);
//排序完成
printf("\n\n排序后:\n%s %d\n", student_arr->name, student_arr->score);
printf("%s %d\n", (student_arr + 1)->name, (student_arr + 1)->score);
printf("%s %d\n", (student_arr + 2)->name, (student_arr + 2)->score);
printf("\n");
printf("\n");
return 0;
}
注意事项:
1.我传入的是结构体指针数组,因为是数组,而且每个元素是结构体指针类型,所以我要将r1转化为结构体二级指针。
2.因为比较的是name,所以我们要再次使用strcmp函数来比较字符串大小。
3.特别注意:我们要知道()的优先级大于成员选择操作符(->),而成员选择操作符(->)的优先级又大于*号,所以我们要使用括号来保证优先级顺序:①r1先于(struct**)结合,②(struct**)r1与*号结合,表示从student_arrx数组中拿出了student1,所以r1现在指向student1,所以我们再使用成员选择操作符(->)就可以访问到这个结构体中的字符串了!但是,上面说了,成员选择操作符(->)的优先级大于*号,所以我们要用括号括起来(*(struct**)r1),再使用->。
代码如下:
//比较函数:
int com_structx1_name(const void* r1, const void* r2)
{
return strcmp((*((struct stu**)r1))->name, (*((struct stu**)r2))->name);
}
int main()
{
struct stu student1 = { 210603,"wangwu",80 };
struct stu student2 = { 210602,"lisi",99 };
struct stu student3 = { 210601,"zhangsan",100 };
struct stu* student_arrx[3] = { &student1,&student2,&student3 };
//排序前
printf("排序前:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);
printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);
printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);
//开始排序
qsort(student_arrx, sizeof(student_arrx) / sizeof(student_arrx[0]), sizeof(student_arrx[0]), com_structx1_name);
//排序后
printf("\n\n排序后:\n%s %d\n", student_arrx[0]->name, student_arrx[0]->score);
printf("%s %d\n", student_arrx[1]->name, student_arrx[1]->score);
printf("%s %d\n", student_arrx[2]->name, student_arrx[2]->score);
//李四第一
//王五第二
//张三第三
return 0;
}
接下来我们来自己模拟实现一个qsort函数,因为我们这个实现qsort函数主要是为了实现qsort函数的通用性,所以我们可以用冒泡排序来实现qsort函数的通用性,(大家也可以使用快排算法再来模拟实现qsort函数)。
实现结果:
代码如下(排序的是上一个例子):
#include
#include
//定义结构体
typedef struct student
{
char name[20];
int score;
}student;
//自定义比较函数 使用strcmp函数比较字符串大小
int compare_student(const void* r1, const void* r2)
{
return strcmp((*((student**)r1))->name, (*((student**)r2))->name);
}
//交换函数,使用char*指针一个字节一个字节交换
void swap(char*r1,char* r2, size_t width)
{
size_t i = 0;
for (i = 0; i < width; i++)
{
char temp = 0;
temp = *r1;
*r1 = *r2;
*r2 = temp;
r1++;
r2++;
}
}
//自定义qsort函数的实现
void My_bubble_qsort(void* base, size_t num, size_t width, int (*compare)(const void*r1 ,const void*r2 ))
{
size_t i = 0;
size_t j = 0;
for (i = 0; i < num-1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{
swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}
}
}
}
int main()
{
student wangwu = { "wangwu",75 };
student lisi = { "lisi",94 };
student zhangsan = { "zhangsan",60 };
student* arr_student[3] = { &wangwu,&lisi,&zhangsan };//定义结构体指针数组
int i = 0;
printf("排序前:\n");
for (i = 0; i < 3; i++)
{
printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);
}
My_bubble_qsort(arr_student, sizeof(arr_student) / sizeof(arr_student[0]),sizeof(arr_student[0]),compare_student );
printf("\n排序后:\n");
for (i = 0; i < 3; i++)
{
printf("%s,%d\n", arr_student[i]->name, arr_student[i]->score);
}
return 0;
}
My_bubble_qsort实现讲解:
1.设置参数:我们将base设置为void*型,num、width设置为size_t(无符号整形),一个返回值为int型,两个参数为void*型的函数指针。
2.在判断条件中,判断是否进行两个数据间的交换,我们设置在只有compare函数返回大于0才进行。
3.传到compare函数中参数设置,这里我们将要比较的类型先转化为char*型,指向一个字节,因为char*型指针指向一个字节,我们不知道传入的类型大小是多少,所以我们可以先设置为char*型指针指向传入的元素我们可以再加上宽度 ,这就可以让char*指针指向下一个数据,如果我们再将(width*j),这就可以传入这一串数据中的第j个元素。这样第一个参数就设置好了,因为冒泡排序是两个相邻的元素比较,所以第二个元素我们只用再j的基础上+1就可以表示下一个元素了。
4.交换函数(swap),当我们发现compare函数的返回值大于1时,我们就要开始进行交换。先将要交换的两个元素以char*型的形式传入,再将width传入swap函数。这时使用char*型指针的优势就体现了出来,因为char*型指针只改变一个字节,所以我们再将width传入,使用char*一个字节一个字节进行交换,直到i
qsort是一个通用性很强的函数,可以排序绝大多数的数据结构,但是关于qsort的使用,仍需要我们大量练习才可以熟练掌握。
好了,本篇文章就到这结束了,感觉不错的朋友门可以留个赞噢~