C进阶:指针的进阶(4)

た 入场券 2023-10-13 19:18 113阅读 0赞

1db8ab180ffa4829be6af2d502d88340.png

回调函数

回调函数就是一个通过函数指针调用的函数。(函数指针的一个非常重要的作用就是实现回调函数)。如果你把这个函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。**回调函数不是由该函数的实现方直接调用,而是在特定的事件或者条件发生时由另一方实现调用的,用于对该事件或条件进行响应。**

举例:比如B函数中有一个参数是A函数的地址,在使用B函数时调用了A函数,我们就说A函数是回调函数。

下面我们通过qsort这一重要的快速排序函数来了解一下回调函数。

  1. //头文件:#include <stdlib.h>
  2. void qsort(void* base,//指向了被排序的第一个函数
  3. size_t sum,//排序的元素个数,size_t指无符号整数
  4. size_t size,//一个元素大小,单位是字节
  5. //函数指针类型——这个函数指针指向一个函数,能够比较base指向的两个函数
  6. //这个回调函数返回小于零的数表明p1<p2
  7. //返回大于零的数表明p1>p2
  8. //返回等于零的数表明p1=p2
  9. int (*cmp)(const void* p1, const void* p2)
  10. );
  11. //void*指针是无类型指针,可以接受任意类型的地址(这也是qsort函数的一个重要的特点)
  12. //(因为这个函数不知道你要排序什么类型的指针,所以用void*)
  13. //1.不能进行解引用操作,2.不能直接进行指针运算

下面来看一下qsort函数的使用(这里使用整型和结构体排序为例)

整型类型排序

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. //注意:这个函数需要自己书写(注意升降序)
  4. int int_cmp(const void* p1, const void* p2)
  5. {
  6. //由于p1,p2都是void*类型的,不能直接进行解引用操作,
  7. //因此将它们转换为int*再解引用获得它们的值
  8. //这个是升序,若想改为降序,只需要将p1,p2的位置互换即可
  9. return (*(int*)p1 - *(int*)p2);
  10. }
  11. print(int sz, int arr[])
  12. {
  13. int i = 0;
  14. for (i = 0; i < sz; i++)
  15. {
  16. printf("%d ", arr[i]);
  17. }
  18. }
  19. void test()
  20. {
  21. int arr[] = { 3,1,4,5,8,6,7,9,0,2 };
  22. int sz = sizeof(arr) / sizeof(arr[0]);
  23. qsort(arr, sz, sizeof(int), int_cmp);
  24. print(sz, arr);
  25. }
  26. int main()
  27. {
  28. test();
  29. return 0;
  30. }

结构体类型排序(按照整型成员)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. struct Peo {
  5. char name[20];
  6. int age;
  7. };
  8. int cmp_byage(const void* p1, const void* p2)
  9. {
  10. return (((struct Peo*)p1)->age - ((struct Peo*)p2) -> age);
  11. }
  12. test1()
  13. {
  14. struct Peo p[] = {
  15. {"zhangsan",20},{"lisi",50},{"wangwu",15}};
  16. int sz = sizeof(p) / sizeof(p[0]);
  17. qsort(p, sz, sizeof(struct Peo), cmp_byage);
  18. }
  19. int cmp_byname(const void* p1, const void* p2)
  20. {
  21. return strcmp(((struct Peo*)p1)->name, ((struct Peo*)p2)->name);
  22. }
  23. int main()
  24. {
  25. test1();
  26. return 0;
  27. }

按照整型成员升序排序结果

906e7abf355e4fa6b3b35f6ebfe37d1c.png

结构体类型排序(按照字符串成员)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. struct Peo {
  5. char name[20];
  6. int age;
  7. };
  8. int cmp_byname(const void* p1, const void* p2)
  9. {
  10. return strcmp(((struct Peo*)p1)->name, ((struct Peo*)p2)->name);
  11. }
  12. void test2()
  13. {
  14. struct Peo p[]= { {"zhangsan",20},{"lisi",50},{"wangwu",15} };
  15. int sz = sizeof(p) / sizeof(p[0]);
  16. qsort(p, sz, sizeof(struct Peo), cmp_byname);
  17. }
  18. int main()
  19. {
  20. test2();
  21. return 0;
  22. }

按照字符串类型成员升序排序结果

247dc5798c2644478200d466cd996805.png

模拟实现

为了帮助我们更好地理解qsort 函数的原理,下面我会带大家来模拟实现一下这个函数。

我们给它起名起名曰:bubble_sort( )

1.使用的这个函数的思想是冒泡排序的思想。

2.它可以适用于各种类型的排序。

要搞清这个函数的具体实现方法,我们首先要思考下面几个问题:

1.对于不同类型的数据,我们万万不能使用简单的数学比较符号来比较。

解决:我们可以将2个元素的比较方法,以函数参数的形式传递,不同类型传不同比较方法。

2.不同数据类型,交换略有差异。

解决:在bubble_sort()函数内部嵌套使用一个swap函数,将比较的两个元素传递过去,由于不知道元素的类型,所以我们以char*的形式传递(因为走过char*的步长最小,我们可以根据元素大小size一个一个字节进行交换)。

下面我们来看一下具体代码(以整型数组的升序排序为例)

  1. #include <stdio.h>
  2. #include <string.h>
  3. //非字符串类型的比较方法
  4. int int_cmp(const void* p1, const void* p2)
  5. {
  6. return (*(int*)p1 - *(int*)p2);
  7. }
  8. //字符串类型的比较方法
  9. //int char_cmp(const void* p1, const void* p2)
  10. //{
  11. // return strcmp(根据两个字符串相关类型传入有关参数);
  12. //}
  13. //交换函数swap的实现
  14. void swap(char* buf1, char* buf2, int size)
  15. {
  16. int i = 0;
  17. char tmp = 0;
  18. for (i = 0; i < size; i++)
  19. {
  20. //由于不知道交换数据的具体单位,所以我么逐字节进行数据交换
  21. tmp = *buf1;
  22. *buf1 = *buf2;
  23. *buf2 = tmp;
  24. buf1++;
  25. buf2++;
  26. }
  27. }
  28. void bubble_sort(void* base, int count, int size, int(*cmp)(void*, void*))
  29. {
  30. //确定交换的趟数
  31. int i = 0;
  32. for (i = 0; i < count - 1; i++)
  33. {
  34. //确定一趟交换类型的次数
  35. int j = 0;
  36. for (j = 0; j < count - 1 - i; j++)
  37. {
  38. //这里假设是升序交换(即>0)
  39. //跟据交换参数的类型不同,将位置定位到第j个和第j+1个元素的位置上
  40. if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
  41. {
  42. swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
  43. }
  44. }
  45. }
  46. }
  47. //打印
  48. void print(int arr[], int sz)
  49. {
  50. int i = 0;
  51. for (i = 0; i < sz; i++)
  52. {
  53. printf("%d ", arr[i]);
  54. }
  55. }
  56. void test()
  57. {
  58. int arr[] = { 3,1,6,4,7,8,2,5,9,0 };
  59. int sz = sizeof(arr) / sizeof(arr[0]);
  60. bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);
  61. print(arr, sz);
  62. }
  63. int main()
  64. {
  65. test();
  66. return 0;
  67. }

好了,这就是回调函数及qsort函数的所有内容,感谢各位未来的大厂员工支持!!!

发表评论

表情:
评论列表 (有 0 条评论,113人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C指针(一)

    > 大家好,我是深鱼~ 【前言】: 指针的主题,在初阶指针章节已经接触过了,我们知道了指针的概念: 1.指针就是个变量,用来存放地址,地址的唯一标识一块内存空间(指针变量