函数指针-如何理解typedef void (*pfun)(void)
问题:
在刚接触typedef void (*pfun)(void) 这个结构的时候,存在疑惑,为什么typedef后只有一“块”东西,而不是两“块”东西呢?那是谁“替代”了谁啊?我总结了一下,一方面是对typedef的概念不清晰,另一方面受了#define的影响,犯了定向思维的错误。
概念理解:
-typedef 只对已有的类型进行别名定义,不产生新的类型;
-# define只是在预处理过程对代码进行简单的替换。
清晰了解两个概念后,发现它们就是两个不同的概念,并没有太多的联系。
类比理解:
typedef unsigned int UINT32; // UINT32 类型是unsigned int
UINT32 sum; // 定义一个变量:int sum;
typedef int arr[3]; // arr 类型是 int[3];(存放int型数据的数组)
arr a; // 定义一个数组:int a[3];
同理:
typedef void (*pfun)(void); // pfun 类型是 void(*)(void)
pfun main; // 定义一个函数:void (*main)(void);
在博客上看到一个经典的函数指针用例:
#include<stdio.h>
typedef int (*FP_CALC)(int, int);
//注意这里不是函数声明而是函数定义,它是一个地址,你可以直接输出add看看
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return b? a/b : -1;
}
//定义一个函数,参数为op,返回一个指针。该指针类型为 拥有两个int参数、
//返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
FP_CALC calc_func(char op)
{
switch (op)
{
case '+' : return add; // 返回函数的地址
case '-' : return sub;
case '*' : return mul;
case '/' : return div;
default:
return NULL;
}
return NULL;
}
//s_calc_func为函数,它的参数是 op,返回值为一个拥有两个int参数、返回类型为int 的函数指针
int (*s_calc_func(char op)) (int, int)
{
return calc_func(op);
}
//最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
int calc(int a, int b, char op)
{
FP_CALC fp = calc_func(op); // 根据预算符得到各种运算的函数的地址
int (*s_fp)(int, int) = s_calc_func(op); // 用于测试
// ASSERT(fp == s_fp); // 可以断言这俩是相等的
if (fp){
return fp(a, b); //根据上一步得到的函数的地址调用相应函数,并返回结果
}
else{
return -1;
}
}
void main()
{
int a = 100, b = 20;
printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
}
结合代码理解:
代码作者在注释中表述得很清楚,个人觉得最有意思就是一下这个函数:
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和指针》的时候,我猛然想起还有一个东西叫“函数指针数组”,也就是书中所描述的新概念:转移表。
下面是实现一个简易计算器的核心代码:
switch(oper){
case ADD:
result = add(oper1, oper2);
break;
case SUB:
result = sub(oper1, oper2);
break;
case MUL:
result = mul(oper1, oper2);
break;
case DIV:
result = div(oper1, oper2);
break;
……
}
这是一种我们常用的实现方式,在书中提到有一个最起码看起来更高端,更简洁的方法:
double add(double, double);
double sub(double, double);
double mul(double, double);
double div(double, double);
……
Double (*oper_fun[])(double, double) = {add, sub,mul,div,…};
// 调用时:
result = oper_func[oper](oper1, oper2);
为什么要调用函数来执行这些操作呢?把具体操作和选择操作的代码分开是一种良好的设计方案。
函数指针-如何理解typedef void (*pfun)(void)的更多相关文章
- 如何理解typedef void (*pfun)(void)
问题: 在刚接触typedef void (*pfun)(void) 这个结构的时候,存在疑惑,为什么typedef后只有一"块"东西,而不是两"块"东西呢?那 ...
- 函数指针的理解 from 数据结构
今天在学习数据结构中遇到一些问题,函数的指针不知道怎么用,给自己科普一哈 1 int LocateElem_Sq(SqList L, LElemType_Sq e, Status(*Compare)( ...
- c语言函数指针的理解与使用
1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun ...
- 指向类成员函数的函数指针及#define typedef 实现类成员函数的类型转换
#include <iostream> using namespace std; class Test { public : void print() { cout << &q ...
- 转·c语言函数指针的理解与使用
原文出处:https://www.cnblogs.com/haore147/p/3647262.html 1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 ...
- c语言函数指针的理解与使用(学习)
1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 2 3 A) char * (*fun1)(char * p1,char * p2); B) char ...
- C语言关于指针函数与函数指针个人理解
1,函数指针 顾名思义,即指向函数的指针,功能与其他指针相同,该指针变量保存的是所指向函数的地址. 假如是void类型函数指针定义方式可以是 void (*f)(参数列表);亦可以先用 typedef ...
- void指针;函数指针
void 类型指针 void => 空类型 void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指针做出正确的 类型转换,然后再间接引用指针.voi ...
- C 中typedef 函数指针的使用
类型定义的语法可以归结为一句话:只要在变量定义前面加上typedef,就成了类型定义.这儿的原本应该是变量的东西,就成为了类型. int integer; //整型变量int *pointer ...
随机推荐
- Porsche Piwis Tester 2 Online Coding Guide
Porsche Piwis online programming account service is piwis porsche subscription and piwis tester 2 on ...
- dalaozouleyeyaojianqiangdehuoxiaqu
dalaozouleyeyaojianqiangdehuoxiaqu 没错,YY又开始哔哔了,非常不淡定,发个博客冷静一下反正没人看 好吧他们还是退役了,关键是我昨天竟然没看到博文???? (我是不会 ...
- Django框架----Object Relational Mapping(ORM)
Django中的ORM Django项目使用MySQL数据库 1. 在Django项目的settings.py文件中,配置数据库连接信息: DATABASES = { "default&qu ...
- Inferred type 'S' for type parameter 'S' is not within its bound;
在使用springboot 方法报错: Inferred type 'S' for type parameter 'S' is not within its bound; should extends ...
- kali linux 压缩文件解压缩命令(包含7z)
tar 解包:tar xvf FileName.tar 打包:tar cvf FileName.tar DirName (注:tar是打包,不是压缩!) ——————————————— .gz 解压1 ...
- mxnet设置动态学习率(learning rate)
https://blog.csdn.net/xiaotao_1/article/details/78874336 如果learning rate很大,算法会在局部最优点附近来回跳动,不会收敛: 如果l ...
- oracle 9i/10gR2所有版本下载地址
Oracle 9i Oracle9i Database Release 2 Enterprise/Standard/Personal Edition for Windows NT/2000/XP ht ...
- WEB后台认证机制
mark to :http://www.cnblogs.com/xiekeli/p/5607107.html HTTP Basic Auth HTTP Basic Auth简单点说明就是每次请求API ...
- Docker 微服务教程(搭建真正的网站)
Docker 是一个容器工具,提供虚拟环境.很多人认为,它改变了我们对软件的认识. 站在 Docker 的角度,软件就是容器的组合:业务逻辑容器.数据库容器.储存容器.队列容器......Docker ...
- 【题解】Luogu P3950 部落冲突
原题传送门 这题用Link-Cut-Tree解决,Link-Cut-Tree详解 我们用Link-Cut-Tree维护连通性(十分无脑) 一开始先把树中每条边的两端连接 U操作:把u,v两个点连起来 ...