• C语言——指针进阶(二)


    目录

    七.函数指针数组

    八.指向函数指针数组的指针

    九.回调函数

    结尾


    七.函数指针数组

    说白了函数指针数组就是函数指针名字后面添加[  ],二者结合成为数组。

    那么这个函数指针数组到底有什么用呢?先不用着急,下面用一个计算器功能代码为大家抛砖引玉。

    这是一个简单的计算器代码,通过选择不同的功能让其进入不同的函数计算数字。

    1. void menu()
    2. {
    3. printf("***********************\n");
    4. printf("*****1.add 2.sub*****\n");
    5. printf("*****3.mul 4.div*****\n");
    6. printf("*****0.exit *****\n");
    7. printf("***********************\n");
    8. }
    9. int Add(int x, int y)
    10. {
    11. return x + y;
    12. }
    13. int Sub(int x, int y)
    14. {
    15. return x - y;
    16. }
    17. int Mull(int x, int y)
    18. {
    19. return x * y;
    20. }
    21. int Div(int x, int y)
    22. {
    23. return x / y;
    24. }
    25. int main()
    26. {
    27. int input = 0;
    28. int x = 0;
    29. int y = 0;
    30. int ret = 0;
    31. do
    32. {
    33. menu();
    34. printf("请选择:");
    35. scanf("%d", &input);
    36. switch (input)
    37. {
    38. case 1:
    39. printf("请输入两个数字:\n");
    40. scanf("%d %d", &x, &y);
    41. ret = Add(x, y);
    42. printf("%d\n", ret);
    43. break;
    44. case 2:
    45. printf("请输入两个数字:\n");
    46. scanf("%d %d", &x, &y);
    47. ret = Sub(x, y);
    48. printf("%d\n", ret);
    49. break;
    50. case 3:
    51. printf("请输入两个数字:\n");
    52. scanf("%d %d", &x, &y);
    53. ret = Mull(x, y);
    54. printf("%d\n", ret);
    55. break;
    56. case 4:
    57. printf("请输入两个数字:\n");
    58. scanf("%d %d", &x, &y);
    59. ret = Div(x, y);
    60. printf("%d\n", ret);
    61. break;
    62. case 0:
    63. printf("退出程序\n");
    64. break;
    65. default :
    66. printf("输入错误,请重新输入:\n");
    67. }
    68. } while (input);
    69. return 0;
    70. }

    但这样我们会发现一个问题,当我们尝试添加更多的函数功能时,switch会变得越来越长,更繁杂了。

    通过分析代码可以知道,其实每个函数的功能并不相同,但是它们所使用的形参和所代表的函数类型都是一样的。 

    接下来我们来修改一下代码:

    先把switch部分删掉,改成函数指针数组。

    可以看到这里面的下标与我们计算器的选择对应不上,这时候可以用NULL来占用数组一个位置。

    然后通过条件if语句来实现函数功能就行了。

    八.指向函数指针数组的指针

    感兴趣可以当作扩展了解一下哈~

    指向函数指针数组的指针,怎么说呢本质是指针,那存储对象就应该是函数指针数组的地址了。

    而我们需要的指针则由(*p)括起来确保是指针而不是数组,这里pfArr简化成p方便辨认。最后就变成了指向这个数组的指针了。重点是指向数组,而不是变成数组。

    九.回调函数

    我们回到switch代码部分进行回调函数的实现:我们可以先封装一个函数,再让代码相似部分调用4次即可。

    这就是回调函数的神奇之处,用函数指针来接受所需要的函数功能,在calc函数里面使用已经接受了功能函数的地址的指针,输入实参。

    进一步理解回调函数:

    老规矩,先用简单的例子帮助大家理解,后面再深入了解qsort的妙用。先用一个经典例子:冒泡排序。

    其实冒泡排序很简单,10个元素该元素最差跑9次交换,n个元素就跑n-1次。每一次跑完下一个元素都会减少一次对比。

    其实这个函数不够通用,它只能排序整型。

    接下来我们就来介绍qsort函数,这个通用排序函数。

    通过这个我们知道两个相同类型数据的比较方法是不同的。

    那么可以推出qsort的第四项形参是提供两个数据的比较方法。我们可以想象把上诉冒泡排序中的9和8分别传输给e1,e2,让它们在cmp里面比较。不过这时候会遇到一个问题,*e1会出错,原因在于void*类型是无法进行解引用的。

    void*的真正作用是接受各种类型的指针:

    所以如果要实现整型比较大小只能先强制转换了。

    cmp的规则是当p1指向的元素>p2指向的元素时返回大于0的数字,相等返回0,小于返回小于0的数字。

    运行代码,排序成功。其实qsort最关键的部分是第四个形参,必须得创建这个数据对应类型的比较方法才可以进行,比如整型用<>来比较,结构体可以用字符串长度来比较等等。

     

    下面我们来测试结构体排序:

    又到了最关键的创造比较函数部分。

     

     再尝试按照名字比较,这时候的名字是字符串,可不能用减号来表示结果了,可以用到strcmp这个函数来比较字符串,巧合的是strcmpd的返回类型与cmp的返回规则一致。

     

    字符串之间的排序是按照字典顺序比较的,比如abc与aq,一开始两个a相同,但后面的q比b大,所以abc排在aq的前面。

    结尾

    本文最精彩的一部分就在于qsort排序了,它不同于我们传统的单一类型的排序,它更加多样性,赋予了我们更多排序的思路。

  • 相关阅读:
    新型飞蛾火焰优化算法-附代码
    Blender:渲染一个简单动画
    ssm和springboot整合
    数字图像处理笔记
    图解 JVM 内存结构
    软考高项(已通过,E类人才)-学习笔记材料梳理汇总
    分割回文串
    Web前端:前端开发人员与后端开发人员,谁最适合你的业务?
    基于SpringBoot+MyBatis实现的个人博客系统(一)
    STC 51单片机47——外部中断控制流水灯
  • 原文地址:https://blog.csdn.net/fax_player/article/details/132779429