伙伴云客服论坛»论坛 S区 S软件开发 查看内容

0 评论

0 收藏

分享

解密C语言qsort函数

目录

    一、前言二、简单冒泡排序法三、qsort函数的使用
      1、qsort函数的介绍2、qsort函数的运用
        2.1、qsort函数排序整型数组2.2、qsort函数排序构造体

    四、利用冒泡排序模仿实现qsort函数五、总结


一、前言

我们初识C语言时,会做过让一个整型数组依照从小到大来排序的问题,我们使用的是冒泡排序法,但是假设我们想要比较其他类型怎么办呢,显然我们当时的代码只适用于简单的整形排序,对于构造体等没办法排序,本篇将引入一个库函数来实现我们希望的顺序。

二、简单冒泡排序法
  1. #include <stdio.h>
  2. int main()
  3. {
  4.         int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  5.         int i = 0;
  6.         int sz = sizeof(arr);
  7.         for (i = 0; i < sz-1; i++)
  8.         {
  9.                 int j = 0;
  10.                 for (j = 0; j < sz-1-i; j++)
  11.                 {
  12.                         int tep = 0;
  13.                         if (arr[j] > arr[j + 1])
  14.                         {
  15.                                 tep = arr[j];
  16.                                 arr[j] = arr[j + 1];
  17.                                 arr[j + 1] = tep;
  18.                         }
  19.                 }
  20.         }
  21.         return 0;
  22. }
复制代码
这是我们最初学习的冒泡排序法,我们假设把其修改一下,能不能比较其它类型的数据呢,结果是肯定的。

三、qsort函数的使用


1、qsort函数的介绍

解密C语言qsort函数-1.png

图片1为整体描绘,下面的图片2、3、4、5、6为图片1的解释。
首先我们可以看出qsort函数不需要返回类型,有四个参数,分别为空指针、无符号型、无符号型、函数指针。图二说明该函数无返回类型。图三说明我们base为要排序中的起始地址,是一个空指针类型,所以我们传首元素地址。图四说明num为元素个数,是一个无符号整型,所以我们传一个无符号整型。图五说明width为每个元素的宽度,单位为字节,是一个无符号整型,所以我们传一个无符号整型。图六说明这个函数指针指向一个含有两个空指针作为参数并且返回类型为int的(比较)函数。这个是qsort的最后一个参数,作用是让我们提供比较大小的规则,所以我们传的时候需要传一个函数地址让库函数中qsort的函数指针来接收。补充:qsort对应的头文件为<stdlib.h>。(比较)函数中的两个参数假设第一个大返回一个大于0的数,假设第二个大返回一个小于0的数,假设相等则返回0。
看完这些,相信你对qsort函数有了初步理解。

2、qsort函数的运用

在这里我们举出两个例子来直观看出qsort的应用方法,分别为整型数组排序和构造体排序。

2.1、qsort函数排序整型数组
  1. #include<stdio.h>
  2. #include <stdlib.h>
  3. int cmp_int(const void* e1, const void* e2)
  4. {
  5.         return *(int*)e1 - *(int*)e2;
  6. }
  7. int main()
  8. {
  9.         int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  10.         int sz = sizeof(arr) / sizeof(arr[0]);
  11.         qsort(arr, sz, sizeof(arr[0]), cmp_int);
  12.         int i = 0;
  13.         for(i = 0;i<sz;i++)
  14.         printf("%d ", arr[i]);
  15. }
复制代码
解密C语言qsort函数-2.png

这样就完成了排序。
疑问:
1.qsort第四个参数(函数地址)为什么不需要传参?
首先:我们要晓得,我们第四个参数传的是一个函数地址(函数名加不加取地址符号都可以表示函数的地址),而不是一个函数,所以就没有传参这个说法。其次:等到库函数qsort用函数指针接收后会停止传参等工作(这是程序内部停止的,我们不需要管)。
2.为什么函数定义里面的参数是空指针类型,而不是特定类型?
因为qsort函数并不晓得你要传的是什么类型,也就是它使用这个函数时不晓得传什么类型的实参,所以统一先传一个空类型地址然后函数用空类型指针接收。
3.空指针是什么意思,怎么用?
空指针就像一个垃圾桶,我们可以对其赋上任意类型的指针,这很方便,但是它不能直接使用,就像我们赋的时候一样,用的时候它并不晓得你是什么类型,它相当于只贮存了一个字节的地址,没有明确的步长(指针+1跳过的字节)和访问权限大小(解引用指针访问的字节个数)。所以当我们用空指针时,需要停止强迫类型转换,转换成我们需要的类型。
4.为什么com_int函数在用的时候要把参数强转为int?
因为当我们使用qsort函数时,我们肯定晓得要停止排序的类型,所以我们需要根据自己的需要的类型来写出对应的函数让qsort函数调用。

2.2、qsort函数排序构造体
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. struct stu
  5. {
  6.         char name[10];
  7.         int age;
  8. };
  9. //排序构造体年龄的回调函数
  10. int cmp_struct_stu_age(const void*e1,const void*e2)
  11. {
  12.         return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
  13. }
  14. //排序构造体姓名的回调函数
  15. int cmp_struct_stu_name(const void* e1, const void* e2)
  16. {
  17.         return strcmp(((struct stu*)e1)->name, ((struct stu*)e1)->name);
  18. }
  19. //运用qsort函数排序构造体年龄
  20. void test1()
  21. {
  22.         struct stu s[3] = { {"xiaoming",30},{"lihua",60},{"wangli",40} };
  23.         int sz = sizeof(s) / sizeof(s[0]);
  24.         qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
  25. }
  26. //运用qsort函数排序构造体名字
  27. void test2()
  28. {
  29.         struct stu s[3] = { {"xiaoming",30},{"lihua",60},{"wangli",40} };
  30.         int sz = sizeof(s) / sizeof(s[0]);
  31.         qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
  32. }
  33. int main()
  34. {
  35.         test1();
  36.         //test2();
  37.         return 0;
  38. }
复制代码
补充:
字符串比较大小使用strcmp函数,对应的头文件为<string.h>,依次从左到右比较字母的ASCII值直到不相等,左边大返回大于0的数,右边大返回小于0的数,直到最后都相等返回0,假设你认真看了最前面关于qsort的介绍,你会发现他们很类似,qsort中用到的回调函数也是需要返回这样的形式。


四、利用冒泡排序模仿实现qsort函数

你对于qsort内部如何执行操作的是否感兴趣呢?你对于qsort中传的那些参数是否存在疑问,不晓得他们有什么作用呢,接着看下面的内容,让我们更进一步理解qsort的工作原理,同时让你的冒泡排序变得具有普遍性。
首先我们先试着用my_qsort函数模仿出qsort函数来对一个整型数组排序。
  1. #include <stdio.h>
  2. int cmp_int(const void* e1, const void* e2)
  3. {
  4.         return *(int*)e1 - *(int*)e2;
  5. }
  6. void swap(char* buf1, char* buf2, int width)
  7. {
  8.         int i = 0;
  9.         for (i = 0; i < width; i++, buf1++, buf2++)
  10.         {
  11.                 char tep = 0;
  12.                 tep = *buf1;
  13.                 *buf1 = *buf2;
  14.                 *buf2 = tep;
  15.         }
  16. }
  17. void my_qsort(const void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
  18. {
  19.         int i = 0;
  20.         for (i = 0; i < sz - 1; i++)
  21.         {
  22.                 int j = 0;
  23.                 for (j = 0; j < sz - 1 - i; j++)
  24.                 {
  25.                         if(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
  26.                         swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
  27.                 }
  28.         }
  29. }
  30. void test3()
  31. {
  32.         int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  33.         int sz = sizeof(arr) / sizeof(arr[0]);
  34.         my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
  35.         int i = 0;
  36.         for (i = 0; i < sz; i++)
  37.                 printf("%d ", arr[i]);
  38. }
  39. int main()
  40. {
  41.         test3();
  42.         return 0;
  43. }
复制代码
以上代码就是我们模仿出的qsort函数,相信看完这个你会对qsort理解更深化,不过有一点需要注意,我们模仿时使用的是冒泡排序,而qsort其实内部使用的是快排,所以其实稍有差距。但大致是一致的。
补充:
1.在my_qsort函数中,我们在比较函数(cmp)中传入的是内容解释:
☃️(char*)base+jwidth 这个实际上就是原始的base是空指针类型,所以我们无法直接使用,但我们也不确定传来的详细是什么类型,因而我们先将其强迫类型转换为char,然后根据指针加几就跳过几个步长个字节来确定每次跳过一个元素。
2.在my_qsort函数中,我们的swap函数也是一个重要部分,它是如何实现的呢?
☃️首先和1刚开端一样,我们传的实参的形式就是为了保证每次可以跳过一个元素,然后当传到swap函数里后,我们不像cmp函数一样有提早准备好的强转类型,因而我们需要用一种特别的方法保证可以互换元素,那么我们会想到元素是怎样贮存的呢,是以二进制以字节为单位贮存的,那么我们假设需要交换元素,是不是只需要把每个字节都交换一次就好了呢?答案是肯定的,于是我们也将传来的地址强迫类型转换为char*,保证每次交换的是一个字节,然后又有一个问题就是我们需要交换几次呢,还记得之前传的那个参数width吗,它是每个元素的宽度,单位是字节,因而我们可以通过一个for循环,修女换width次来使得一个元素所占的字节全部交换。
我们这一部分虽然是用模仿的qsort来排序整型数组,但其实假设这个学会了的话,构造体和前面说的是一模一样的,因为模仿的qsort不用变,只需要改一下cmp函数的类型即可。
完好代码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. //排序整型数组顺序时的回调函数
  5. int cmp_int(const void* e1, const void* e2)
  6. {
  7.         return *(int*)e1 - *(int*)e2;
  8. }
  9. //声明构造体类型
  10. struct stu
  11. {
  12.         char name[10];
  13.         int age;
  14. };
  15. //排序构造体年龄顺序时的回调函数
  16. int cmp_struct_stu_age(const void*e1,const void*e2)
  17. {
  18.         return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
  19. }
  20. //排序构造体名字顺序时的回调函数
  21. int cmp_struct_stu_name(const void* e1, const void* e2)
  22. {
  23.         return strcmp(((struct stu*)e1)->name, ((struct stu*)e1)->name);
  24. }
  25. //运用qsort函数排序整型数组
  26. void test1()
  27. {
  28.         int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  29.         int sz = sizeof(arr) / sizeof(arr[0]);
  30.         qsort(arr, sz, sizeof(arr[0]), cmp_int);
  31. }
  32. //运用qsort函数排序构造体
  33. void test2()
  34. {
  35.         struct stu s[3] = { {"xiaoming",30},{"lihua",60},{"wangli",40} };
  36.         int sz = sizeof(s) / sizeof(s[0]);
  37.         qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
  38.         //qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
  39. }
  40. //模仿qsort中的交换函数
  41. void swap(char* buf1, char* buf2, int width)
  42. {
  43.         int i = 0;
  44.         for (i = 0; i < width; i++, buf1++, buf2++)
  45.         {
  46.                 char tep = 0;
  47.                 tep = *buf1;
  48.                 *buf1 = *buf2;
  49.                 *buf2 = tep;
  50.         }
  51. }
  52. //模仿qsort
  53. void my_qsort(const void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
  54. {
  55.         int i = 0;
  56.         for (i = 0; i < sz - 1; i++)
  57.         {
  58.                 int j = 0;
  59.                 for (j = 0; j < sz - 1 - i; j++)
  60.                 {
  61.                         if(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
  62.                         swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
  63.                 }
  64.         }
  65. }
  66. //运用模仿qsort函数(my_qsort)排序整型数组
  67. void test3()
  68. {
  69.         int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
  70.         int sz = sizeof(arr) / sizeof(arr[0]);
  71.         my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
  72.         int i = 0;
  73.         for (i = 0; i < sz; i++)
  74.                 printf("%d ", arr[i]);
  75. }
  76. //运用模仿qsort函数(my_qsort)排序构造体
  77. void test4()
  78. {
  79.         struct stu s[3] = { {"xiaoming",30},{"lihua",60},{"wangli",40} };
  80.         int sz = sizeof(s) / sizeof(s[0]);
  81.         my_qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
  82.         //qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
  83. }
  84. int main()
  85. {
  86.         //test1();  //用qsort实现整型数组排序
  87.         //test2();  //用qsort实现构造体排序
  88.         test3();    //用模仿qsort函数实现整型数组排序
  89.         //test(4)   //用模仿qsort函数实现构造体排序
  90.         return 0;
  91. }
复制代码
回调函数:回调函数就是一个通过函数指针调用的函数。假设你把函数指针(地址)作为参数传送给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,用于对该事件或条件停止响应。
通常来讲就是:把这个函数地址作为参数传过去,另一个函数用函数指针接收然后再调用这个函数,那么这个被调用的函数就叫回调函数。(通过函数指针调用的这个函数,叫做回调函数)

五、总结

到此这篇关于你真的理解C语言qsort函数吗带你深度剖析qsort函数的文章就介绍到这了,更多相关c语言qsort函数内容请搜索网站以前的文章或继续阅读下面的相关文章希望大家以后多多支持网站!

回复

举报 使用道具

全部回复
暂无回帖,快来参与回复吧
本版积分规则 高级模式
B Color Image Link Quote Code Smilies

战哥
注册会员
主题 21
回复 21
粉丝 0
|网站地图
快速回复 返回顶部 返回列表