• 深入理解指针(四)


    目录

    1. 回调函数是什么?

    ​2. qsort使用举例

    2.1冒泡排序

    2.2使用qsort函数排序整型数据

    ​2.3 使用qsort排序结构数据(名字)

    2.4 使用qsort排序结构数据(年龄)

    3. qsort函数的模拟实现


    1. 回调函数是什么?

    回调函数就是⼀个通过函数指针调⽤的函数。

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

    上述图片中的代码是之前计算器的代码,这种代码比较冗余,我们可以把重复的代码写成一个函数。

    1. #include
    2. void menu()
    3. {
    4. printf("**************************\n");
    5. printf("***** 1.Add 2.Sub *****\n");
    6. printf("***** 3.Mul 4.Div *****\n");
    7. printf("***** 0.Exit *****\n");
    8. printf("**************************\n");
    9. }
    10. int Add(int x, int y)
    11. {
    12. return x + y;
    13. }
    14. int Sub(int x, int y)
    15. {
    16. return x - y;
    17. }
    18. int Mul(int x, int y)
    19. {
    20. return x * y;
    21. }
    22. int Div(int x, int y)
    23. {
    24. return x / y;
    25. }
    26. void Calc(int(*pf)(int, int))
    27. {
    28. int x = 0;
    29. int y = 0;
    30. int index = 0;
    31. printf("请输入两个操作数:>");
    32. scanf("%d %d", &x, &y);
    33. index = pf(x, y);
    34. printf("%d\n", index);
    35. }
    36. int main()
    37. {
    38. int input = 0;
    39. do
    40. {
    41. menu();//菜单
    42. printf("请输入:>");
    43. scanf("%d", &input);
    44. switch (input)
    45. {
    46. case 1:
    47. Calc(Add);
    48. break;
    49. case 2:
    50. Calc(Sub);
    51. break;
    52. case 3:
    53. Calc(Mul);
    54. break;
    55. case 4:
    56. Calc(Div);
    57. break;
    58. case 0:
    59. printf("退出计算器!!!\n");
    60. break;
    61. default:
    62. printf("输入错误,请重新输入!!!\n");
    63. break;
    64. }
    65. } while (input);
    66. return 0;
    67. }

    输出结果:

    2. qsort使用举例

    qsort是C语言中的一个库函数,这个函数是用来对任意数据进行排序。

    2.1冒泡排序

    我们之前学习过一种排序,叫冒泡排序,冒泡排序的思想就是两两比较。

    1. #include
    2. void bubble_sort(int* arr, int sz)
    3. {
    4. for (int i = 0; i < sz - 1; i++)
    5. {
    6. for (int j = 0; j < sz - i - 1; j++)
    7. {
    8. if (*(arr + j) > *(arr + j + 1))
    9. {
    10. int tem = *(arr + j);
    11. *(arr + j) = *(arr + j + 1);
    12. *(arr + j + 1) = tem;
    13. }
    14. }
    15. }
    16. }
    17. void print_arr(int* arr, int sz)
    18. {
    19. for (int i = 0; i < sz; i++)
    20. {
    21. printf("%d ", *(arr + i));
    22. }
    23. }
    24. int main()
    25. {
    26. int arr[] = { 2,6,3,4,1,9,10,8,7,5 };
    27. int sz = sizeof(arr) / sizeof(arr[0]);
    28. bubble_sort(arr, sz);
    29. print_arr(arr, sz);
    30. return 0;
    31. }

    这种代码的功能比较单一,只能排序整型,如果想要排序浮点类型的数据或者其它类型的数据,函数的参数就比如重新设计了,而qsort函数能排序任何类型的数据。

    2.2使用qsort函数排序整型数据

    qsort:

    qsort - C++ Reference

    void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));

     所以qsort的第四个参数就是一个函数,让我们传一个比较大小的函数进去即可,比如我现在想要比较结构体的数据,那么我就需要往进传一个两个结构体成员比较大小的函数,这样qsort就能实现想要排序什么数据就可以排序什么数据。

    排序整型的代码:

    1. #include
    2. #include
    3. /*
    4. p1指向的元素大于p2指向的元素就返回大于0的数字
    5. p1指向的元素小于p2指向的元素就返回小于0的数字
    6. p1指向的元素等于p2指向的元素就返回0
    7. */
    8. //排序整型数据就得提供两个整型的比较函数
    9. int cpm_int(const void*p1, const void*p2)
    10. {
    11. //void*的指针不能解引用,必须强转,比较整型那就强转为int*
    12. return *(int*)p1 - *(int*)p2;
    13. }
    14. int main()
    15. {
    16. int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
    17. int sz = sizeof(arr) / sizeof(arr[0]);
    18. qsort(arr, sz, sizeof(arr[0]), cpm_int);
    19. for (int i = 0; i < 10; i++)
    20. printf("%d ", *(arr + i));
    21. return 0;
    22. }

    输出结果:

    2.3 使用qsort排序结构数据(名字)

    使用qsort排序结构体之前,先看一个操作符,->操作符。

    1. #include
    2. struct Stu
    3. {
    4. char name[20];
    5. int age;
    6. float light;
    7. };
    8. /*
    9. 结构体成员访问操作符
    10. . 结构体变量.成员名
    11. -> 结构体指针->成员名
    12. */
    13. int main()
    14. {
    15. struct Stu s = { "lixiangsi",20,170.0f };
    16. //得到结构体的变量名就用.操作符找结构体的每个成员
    17. printf("%s %d %f\n", s.name, s.age, s.light);
    18. //那如果现在得到的是结构体的地址呢?
    19. struct Stu* ps = &s; //struct Stu*是结构体指针类型
    20. //当得到结构体地址的时候,解引用再用.操作符找结构体每个成员
    21. printf("%s %d %f\n",(*ps).name, (*ps).age,(*ps).light);
    22. //->操作符的使用方法 得到结构体的地址也可以使用->操作符找到结构体的每个成员
    23. printf("%s %d %f\n",ps->name,ps->age,ps->light);
    24. return 0;
    25. }

    输出结果:

    排序结构体数据代码:

    1. #include
    2. #include
    3. #include
    4. struct Stu
    5. {
    6. char name[20];
    7. int age;
    8. float light;
    9. };
    10. int cmp_name(const void*p1, const void*p2)
    11. {
    12. return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
    13. //strcmp这个函数中p1大于p2返回1,p1小于p2返回-1,p1等于p2返回0
    14. //刚好是我们需要的返回值,所以将strcmp的结果直接返回就行
    15. }
    16. int main()
    17. {
    18. struct Stu s[] = { {"zhangsan",20,181.3f},{"lisi",18,175.5f},
    19. {"wangwu",25,178.8f} };
    20. int sz = sizeof(s) / sizeof(s[0]);
    21. qsort(s, sz, sizeof(s[0]), cmp_name);
    22. for (int i = 0; i < sz; i++)
    23. printf("%s %d %f\n", s[i].name, s[i].age, s[i].light);
    24. return 0;
    25. }

    代码运行结果:

     从运行结果可以看出按名字排序,lisi最小,wangwu次之,zhangsan最大,因为lisi的首字母l的ASCILL码值最小,w次之,z最大。

    关于字符串怎么比较大小的方式可参考文章字符函数和字符串函数:字符函数和字符串函数-CSDN博客文章浏览阅读577次,点赞27次,收藏13次。在编程的过程中,我们经常要处理字符,为了⽅便操作字符,C语⾔标准库中提供了 ⼀系列操作字符的库函数。https://blog.csdn.net/m0_74271757/article/details/139031604?spm=1001.2014.3001.5501

    参考文章中的6. strcmp 的使用和模拟实现章节

    2.4 使用qsort排序结构数据(年龄)

    1. #include
    2. #include
    3. struct Stu
    4. {
    5. char name[20];
    6. int age;
    7. float light;
    8. };
    9. int cmp_age(const void* p1, const void* p2)
    10. {
    11. return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
    12. }
    13. int main()
    14. {
    15. struct Stu s[] = { {"zhangsan",20,181.3f},{"lisi",18,175.5f},
    16. {"wangwu",25,178.8f} };
    17. int sz = sizeof(s) / sizeof(s[0]);
    18. qsort(s, sz, sizeof(s[0]), cmp_age);
    19. for (int i = 0; i < sz; i++)
    20. printf("%s %d %f\n", s[i].name, s[i].age, s[i].light);
    21. return 0;
    22. }

    输出结果:

    我们可以看到qsort函数默认排的是升序,那怎么降序呢?

    我们只需要将我们的比较大小的函数返回值颠倒一下就可以,比如说第一个数比第二个数大,本来返回大于0的时候,我们返回小于0的数字,第一个数比第二个数小,本来返回小于0的数字,我们返回大于0的数字即可。

    升序前面的大于后面的返回大于0的数字,就交换,现在降序的话前面的大于后面的就不需要交换,所以我们返回小于0的数字,相反,升序的话 前面小于后面的就不交换,但是降序的话前面小于后面的就需要交换,就返回大于0的数字。

    3. qsort函数的模拟实现

    我们可以将我们的冒泡排序函数bubble_sort函数改造成通用的算法,可以排序任意类型。

    模仿qsort函数,只不过qsort底层用的是快速排序算法,而我们用冒泡排序算法实现。

    函数参数以及返回值的设计:

     函数体的设计:

    交换元素的代码设计:

    my_qsort排序整型代码:

    1. #include
    2. int cmp(const void* p1, const void* p2)
    3. {
    4. return *(int*)p1 - *(int*)p2;
    5. }
    6. void swap(char* buf1, char* buf2,int size)
    7. {
    8. for (int i = 0; i < size; i++)
    9. {
    10. char tem = *((char*)buf1 + i);
    11. *((char*)buf1 + i) = *((char*)buf2 + i);
    12. *((char*)buf2 + i) = tem;
    13. }
    14. }
    15. void my_qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*))
    16. {
    17. for (int i = 0; i < num - 1; i++)
    18. {
    19. for (int j = 0; j < num - i - 1; j++)
    20. {
    21. if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
    22. {
    23. swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
    24. }
    25. }
    26. }
    27. }
    28. int main()
    29. {
    30. int arr[10] = { 8,2,6,4,3,7,9,1,5,10 };
    31. int sz = sizeof(arr) / sizeof(arr[0]);
    32. my_qsort(arr, sz, sizeof(arr[0]), cmp);
    33. for (int i = 0; i < sz; i++)
    34. printf("%d ", *(arr + i));
    35. return 0;
    36. }

    输出结果:

    my_qsort函数排序结构体代码:

    1. #include
    2. struct Stu
    3. {
    4. char name[20];
    5. int age;
    6. float light;
    7. };
    8. int cmp_age(const void* p1, const void* p2)
    9. {
    10. return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
    11. }
    12. void swap(char* buf1, char* buf2,int size)
    13. {
    14. for (int i = 0; i < size; i++)
    15. {
    16. char tem = *((char*)buf1 + i);
    17. *((char*)buf1 + i) = *((char*)buf2 + i);
    18. *((char*)buf2 + i) = tem;
    19. }
    20. }
    21. void my_qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*))
    22. {
    23. for (int i = 0; i < num - 1; i++)
    24. {
    25. for (int j = 0; j < num - i - 1; j++)
    26. {
    27. if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
    28. {
    29. swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
    30. }
    31. }
    32. }
    33. }
    34. int main()
    35. {
    36. struct Stu s[] = { {"zhangsan",20,167.0f},{"lisi",22,175.5f},{"wangwu",25,180.0f} };
    37. int sz = sizeof(s) / sizeof(s[0]);
    38. my_qsort(s, sz, sizeof(s[0]), cmp_age);
    39. for (int i = 0; i < sz; i++)
    40. printf("%s %d %f\n", s[i].name,s[i].age,s[i].light);
    41. return 0;
    42. }

    输出结果:

    泛型编程里面大多数都是void*的指针。

    qsort就是典型的用了回调函数的场景。

  • 相关阅读:
    IO流内容总结
    Flink 报错:写入数据到流加载失败
    linux高性能服务器
    [大三上20231016]JavaEE SpringBoot
    vue3-web端文件下载
    Java 12 及Tomcat 部署配置
    Node.js中的缓存策略和缓存技巧
    Math类 Random类 System类
    Bert和LSTM:情绪分类中的表现
    C# OpenCvSharp 环形文字处理 直角坐标与极坐标转换
  • 原文地址:https://blog.csdn.net/m0_74271757/article/details/139706047