问题:

在刚接触typedef void (*pfun)(void) 这个结构的时候,存在疑惑,为什么typedef后只有一“块”东西,而不是两“块”东西呢?那是谁“替代”了谁啊?我总结了一下,一方面是对typedef的概念不清晰,另一方面受了#define的影响,犯了定向思维的错误。

概念理解:

-typedef 只对已有的类型进行别名定义,不产生新的类型;

-# define只是在预处理过程对代码进行简单的替换。

清晰了解两个概念后,发现它们就是两个不同的概念,并没有太多的联系。

类比理解:

  1. typedef unsigned int UINT32; // UINT32 类型是unsigned int
  2. UINT32 sum; // 定义一个变量:int sum;
  3. typedef int arr[3]; // arr 类型是 int[3];(存放int型数据的数组)
  4. arr a; // 定义一个数组:int a[3];

同理:

  1. typedef void (*pfun)(void); // pfun 类型是 void(*)(void)
  2. pfun main; // 定义一个函数:void (*main)(void);

在博客上看到一个经典的函数指针用例:

  1. #include<stdio.h>
  2. typedef int (*FP_CALC)(int, int);
  3. //注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看
  4. int add(int a, int b)
  5. {
  6. return a + b;
  7. }
  8. int sub(int a, int b)
  9. {
  10. return a - b;
  11. }
  12. int mul(int a, int b)
  13. {
  14. return a * b;
  15. }
  16. int div(int a, int b)
  17. {
  18. return b? a/b : -1;
  19. }
  20. //定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、
  21. //返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
  22. FP_CALC calc_func(char op)
  23. {
  24. switch (op)
  25. {
  26. case '+' : return add; // 返回函数的地址
  27. case '-' : return sub;
  28. case '*' : return mul;
  29. case '/' : return div;
  30. default:
  31. return NULL;
  32. }
  33. return NULL;
  34. }
  35. //s_calc_func为函数,它的参数是 op,返回值为一个拥有两个int参数、返回类型为int 的函数指针
  36. int (*s_calc_func(char op)) (int, int)
  37. {
  38. return calc_func(op);
  39. }
  40. //最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
  41. int calc(int a, int b, char op)
  42. {
  43. FP_CALC fp = calc_func(op); // 根据预算符得到各种运算的函数的地址
  44. int (*s_fp)(int, int) = s_calc_func(op); // 用于测试
  45. // ASSERT(fp == s_fp); // 可以断言这俩是相等的
  46. if (fp){
  47. return fp(a, b); //根据上一步得到的函数的地址调用相应函数,并返回结果
  48. }
  49. else{
  50. return -1;
  51. }
  52. }
  53. void main()
  54. {
  55. int a = 100, b = 20;
  56. printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
  57. printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
  58. printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
  59. printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
  60. }

结合代码理解:

代码作者在注释中表述得很清楚,个人觉得最有意思就是一下这个函数:

FP_CALC calc_func(char op) <--> int (*calc_func(char op)) (int, int)

  代码作者试图在断言中说明这个关系,相比较,还是FP_CALC calc_func(char op)函数更能表达编码者的意图:calc_func函数返回FP_CALC类型的指针,是一个函数指针,这个函数的形式是int (函数名)(int, int),代码中int add(int a, int b)、int sub(int a, int b)…正是这样的格式。

在阅读《C和指针》的时候,我猛然想起还有一个东西叫“函数指针数组”,也就是书中所描述的新概念:转移表。

下面是实现一个简易计算器的核心代码:

  1. switch(oper){
  2.   case ADD:
  3.     result = add(oper1, oper2);
  4.     break;
  5.   case SUB:
  6.     result = sub(oper1, oper2);
  7.     break;
  8.   case MUL:
  9.     result = mul(oper1, oper2);
  10.     break;
  11.   case DIV:
  12.     result = div(oper1, oper2);
  13.     break;
  14.   ……
  15. }

这是一种我们常用的实现方式,在书中提到有一个最起码看起来更高端,更简洁的方法:

  1. double add(double, double);
  2. double sub(double, double);
  3. double mul(double, double);
  4. double div(double, double);
  5. ……
  6. Double (*oper_fun[])(double, double) = {add, sub,mul,div,…};
  7. // 调用时:
  8. result = oper_func[oper](oper1, oper2);

为什么要调用函数来执行这些操作呢?把具体操作和选择操作的代码分开是一种良好的设计方案。

更多参考

函数指针-如何理解typedef void (*pfun)(void)的更多相关文章

  1. 如何理解typedef void (*pfun)(void)

    问题: 在刚接触typedef void (*pfun)(void) 这个结构的时候,存在疑惑,为什么typedef后只有一"块"东西,而不是两"块"东西呢?那 ...

  2. 函数指针的理解 from 数据结构

    今天在学习数据结构中遇到一些问题,函数的指针不知道怎么用,给自己科普一哈 1 int LocateElem_Sq(SqList L, LElemType_Sq e, Status(*Compare)( ...

  3. c语言函数指针的理解与使用

    1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun ...

  4. 指向类成员函数的函数指针及#define typedef 实现类成员函数的类型转换

    #include <iostream> using namespace std; class Test { public : void print() { cout << &q ...

  5. 转·c语言函数指针的理解与使用

    原文出处:https://www.cnblogs.com/haore147/p/3647262.html 1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 ...

  6. c语言函数指针的理解与使用(学习)

    1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 2 3 A) char * (*fun1)(char * p1,char * p2); B) char  ...

  7. C语言关于指针函数与函数指针个人理解

    1,函数指针 顾名思义,即指向函数的指针,功能与其他指针相同,该指针变量保存的是所指向函数的地址. 假如是void类型函数指针定义方式可以是 void (*f)(参数列表);亦可以先用 typedef ...

  8. void指针;函数指针

    void 类型指针 void => 空类型 void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指针做出正确的 类型转换,然后再间接引用指针.voi ...

  9. C 中typedef 函数指针的使用

    类型定义的语法可以归结为一句话:只要在变量定义前面加上typedef,就成了类型定义.这儿的原本应该是变量的东西,就成为了类型. int integer;     //整型变量int *pointer ...

随机推荐

  1. JavaScript:原生JS实现Facebook实时消息抓捕

    基础知识准备: HTML5给我们提供了一个新的对象叫作:MutationObserver.为了兼容,还有WebKitMutationObserver.MozMutationObserver,挂靠在wi ...

  2. kali linux中文输入法

    kali linux中文输入法 已经使用kali linux有一段时间来,越用越喜欢,真的奥! 最近又有宝宝问我kali linux的中文输入法,鉴于当初在坑里蹲了很长时间,还是记录一下吧! The ...

  3. 数据分析之pandas02

    DataFrame 一.DataFrame DataFrame是一个[表格型]的数据结构.DataFrame又按一定顺序排列的多列数据组成,设计初衷是将Series的使用场景从一维扩展到多维.Date ...

  4. USB概述

    源: USB

  5. ansible中的playbook详解

    首先简单说明一下playbook,playbook是什么呢?根本上说playbook和shell脚本没有任何的区别,playbook就像shell一样,也是把一堆的命令组合起来,然后加入对应条件判断等 ...

  6. gcahce事物不够,借助binlog追上

    gcahce事物不够,借助binlog追上 宕机节点以单机集群启动,既自己作为一个集群启动,不过UUID要和旧的集群保持一致: 修复grastate.dat 文件的方式这里略,直接通过wsrep_re ...

  7. Golang字符串函数认识(一)

    package main import ( "fmt" "strings" "strconv" ) func main(){ //返回字符串 ...

  8. webstorm 安装与基本使用

    1.1 webstorm 安装与配置 1.安装: https://blog.csdn.net/jiangxinyu50/article/details/79104016 2.使用: https://w ...

  9. Codeforces Round #425 (Div. 2) Problem A Sasha and Sticks (Codeforces 832A)

    It's one more school day now. Sasha doesn't like classes and is always bored at them. So, each day h ...

  10. Plantuml画图工具

    1,Plantuml画图工具 安装指南: Mac sublimetext http://blog.csdn.net/zhangjikuan/article/details/53365730 win i ...