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

墨蓝 2023-10-13 18:23 279阅读 0赞

" class="reference-link">b585bc531845471184c684be32dc4945.png

函数指针

首先来看一段代码:

  1. #include <stdio.h>
  2. void test()
  3. {
  4. printf("hehe\n");
  5. }
  6. int main()
  7. {
  8. printf("%p\n", test);
  9. printf("%p\n", &test);
  10. return 0;
  11. }

让我们来看一下执行结果吧:

86f03d98638b4a61a9c86ec3b88a2984.png

从上述结果得出,无论是函数名还是&函数名都是函数的地址,所以我们可以通过这两个获得函数的地址

那么我们就会想到,既然这玩意跟函数一样都有地址,那是不是也可以存在一种指针来存放函数的地址呢?这就用到了我们今天要学习的函数指针。

下面我们看代码:

  1. void test()
  2. {
  3. printf("hehe\n");
  4. }
  5. int main()
  6. {
  7. //下面pfun1和pfun2哪个有能力存放test函数的地址?
  8. void (*pfun1)();
  9. void* pfun2();
  10. return 0;
  11. }

首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针呢?

显然是pfun1,类似于数组指针。pfun1先和*结合,说明pfun1是指针,指针指向的是一个参数,指向的函数无参数,返回值类型是void

我们下面举个例子来展示一下函数指针的使用。

  1. #include <stdio.h>
  2. int Add(int x, int y)
  3. {
  4. return x + y;
  5. }
  6. int main()
  7. {
  8. int (*pf)(int, int) = &Add;
  9. //1 常规的调用函数方法
  10. int a = Add(3, 5);
  11. //2 利用函数的地址调用函数
  12. int b = (*pf)(4, 5);
  13. //3 也是利用函数的地址调用函数
  14. int c = pf(5, 5);
  15. printf("%d\n%d\n%d\n", a, b, c);
  16. return 0;
  17. }

下面我们再阅读两段有趣(用陕西话应该说是木乱)的代码:

//代码一:

(*(void (*)( ) ) 0 ))( );

我们采用由里向外的方法来看,我们不难看出void(*) ( )是函数指针类型,后面跟个“0”那就显然是将0强制转换成void(*)( )类型的函数指针,然后显然就是调用0地址处的这个函数。

//代码二:

void (*signal(int , void(*)(int)))(int)

逐步分析:1.signal是个函数类型

2.signal的函数分别是int和void(*)(int)的这个函数指针类型,该函数指针有一个是int型参数,返回值为void

3.signal函数返回类型也是void(*)(int)

但是代码二又显得过于复杂了,不方便阅读,这时我们可以回首掏出我们

之前在结构体中学到的typedef(重命名)关键字,先看下面的内容:

typedef int(* parr_t)(int);//将int(*)(int)类型重名为parr_t.

我们照猫画虎使用一下上面的方法:

typedef void(* pfun_t)(int);//将void(*)(int)类型转换为pfun_t.

pfun_t signal(int, pfun_t);//将signal函数内的参数void(*)(int)转换为pfun_t,然后signal函数的类型也是void(*)(int)

函数指针数组

倘若君觉得函数指针的难度是:就这?那么我要从兜里掏出函数指针数组,阁下该如何应对呢?

函数指针数组,顾名思义,就是每个元素类型为函数指针的数组

那要把函数的地址存放到一个地址中,那这个数组就叫做函数指针数组,那函数指针数组应该如何定义呢?我们来看一下三种情况:

  1. int (*parr1[10])();
  2. int* parr2[10]();
  3. int (*)() parr3[10];

答案是:parr1 parr1先和[ ]结合,说明parr1是数组,那么数组的每个类型是int(*)( )类型的函数指针。

为了详细了解函数指针数组,我们将引入它的一个重要用途:转移表

例子:计算器。

在以前,我们可能会这样写计算器:

  1. #include<stdio.h>
  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. int main()
  27. {
  28. int x, y;
  29. int input = 0;
  30. int ret = 0;
  31. do {
  32. menu();
  33. printf("请选择:>");
  34. scanf("%d", &input);
  35. switch (input)
  36. {
  37. case 1:
  38. printf("请输入操作数:");
  39. scanf("%d %d", &x, &y);
  40. ret = add(x, y);
  41. printf("ret = %d\n", ret);
  42. break;
  43. case 2:
  44. printf("请输入操作数:");
  45. scanf("%d %d", &x, &y);
  46. ret = sub(x, y);
  47. printf("ret = %d\n", ret);
  48. break;
  49. case 3:
  50. printf("请输入操作数:");
  51. scanf("%d %d", &x, &y);
  52. ret = mul(x, y);
  53. printf("ret = %d\n", ret);
  54. break;
  55. case 4:
  56. printf("请输入操作数:");
  57. scanf("%d %d", &x, &y);
  58. ret = div(x, y);
  59. printf("ret = %d\n", ret);
  60. break;
  61. case 0:
  62. printf("单击以推出程序:\n");
  63. break;
  64. default:
  65. printf("输入错误,请重新输入\n");
  66. break;
  67. }
  68. }while(input);
  69. return 0;
  70. }

这样写不仅重复代码量多,而且非常木乱。

因此我们来使用函数指针数组来对这个计算器进行改造。

  1. int main()
  2. {
  3. int x, y;
  4. int input = 0;
  5. //将四个函数以及零的地址放入函数指针数组当中
  6. int (*parr[5])(int, int) = { 0,add,sub,mul,div };
  7. do
  8. {
  9. menu();
  10. printf("请输入您的选择:>\n");
  11. scanf("%d", &input);
  12. //根据input值判断行为
  13. if (input >= 1 && input <= 4)
  14. {
  15. //正常进入计算器操作程序
  16. printf("请输入两个操作数:\n");
  17. scanf("%d %d", &x, &y);
  18. //通过函数指针数组的元素索引找到对应使用函数元素的地址,并在后面传参
  19. int ret = (*parr[input])(x, y);
  20. printf("ret = %d\n", ret);
  21. }
  22. else if (input == 0)
  23. {
  24. printf("退出计算器\n");
  25. }
  26. else
  27. {
  28. printf("选择错误,请重新选择:>\n");
  29. }
  30. } while (input);
  31. return 0;
  32. }

这样做无疑大大减少了代码量,而且程序的可阅读性非常高,这便是函数指针数组的重要使用。

指向函数指针的数组

倘若观众老爷还是觉得简单,那我要是拿出指向函数指针的数组,阁下该如何应对?

定义:指向函数指针数组的指针是一个指针。

指针指向一个数组,数组的元素都是函数指针。

如何定义?

  1. #include <stdio.h>
  2. void test(const char* str)
  3. {
  4. printf("%s", str);
  5. }
  6. int main()
  7. {
  8. //函数指针pfun
  9. void (*pfun)(const char*) = test;
  10. //函数指针数组pfunArr
  11. void (*pfunArr[5])(const char*);
  12. pfunArr[0] = test;
  13. //指向函数指针数组pfunArr的指针ppfunArr
  14. void (*(*ppfunArr)[10])(const char*) = &pfunArr;
  15. return 0;
  16. }

#

好了这期就介绍到这里,下一期我们来详细讲一下回调函数和qsort函数,指针的知识就圆满结束啦!谢谢各位未来的大厂员工收看,谢谢!!!

发表评论

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

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

相关阅读

    相关 C指针(一)

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