打印
[软件资料]

函数指针与回调函数

[复制链接]
429|32
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
mattlincoln|  楼主 | 2022-11-23 22:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
函数指针是指向函数的指针变量。通过函数指针C语言可以实现各种强大的功能与设计方法。而回调函数是函数指针最常见的用途,是C语言的重中之重,也是C语言面试当中的必考知识点和难点。
在我们平时开发STM32或者其它单片机时,我们经常都会用到原厂提供的固件库函数,固件库函数中有非常多回调函数。那么什么是回调函数呢?回调函数是作为参数传递给另一个函数的函数。接受回调作为参数的函数预计会在某个时间点执行它。回调机制允许下层软件层调用上层软件层定义的函数。

应用程序代码和硬件驱动程序之间的交互
硬件驱动程序是一个独立的可重用驱动程序,它不了解上面的层(用户应用程序)。硬件驱动程序提供API函数,允许用户应用程序将函数注册为回调。然后,此回调函数由硬件驱动程序作为执行的一部分进行调用。如果不使用回调,就会被编码为直接调用。这将使硬件驱动程序特定于特定的高级软件级别,并降低其可重用性。回调机制的另一个好处是,在程序执行期间可以动态更改被调用的回调函数。
一、函数指针
函数指针,顾名思义它就是一个指针,只不过它是一个函数指针,所以指向的是一个函数。类比一般的变量指针,指针变量,实质上是一个变量,只不过这个变量存放的是一个地址,在32位单片机中,任何类型的指针变量都存放的是一个大小为4字节的地址。
int   a; < = > void cal_sum(void);int * p; < = > void (*func_ptr)(void);p=&a;   < = > func_ptr= &cal_sum;
左边走义变量a,右边定义函数cal_sum;
左边定义int指针,右边定义func_ptr;
左边赋值指针,右边赋值函数指针;
可能这样大家还是不太清楚,我是搞嵌入式单片机的,有本事你在Keil中给我举一个例子啊?
可以啊,没问题,请看!
#include "sys.h"#include "led.h"#include "delay.h"#include "usart.h"uint8_t cal_sum(uint8_t a, uint8_t b){    return a + b;}int main(void){    delay_init();    uart_init(9600);    uint8_t a = 10;    uint8_t b = 8;    /*定义一个函数指针*/    uint8_t (*func_ptr)(uint8_t, uint8_t);    /*将函数名赋值给函数指针*/    func_ptr = cal_sum;    printf("cal_sum_address  =0x%p\r\n", cal_sum);    printf("func_ptr_address =0x%p\r\n", func_ptr);    printf("%d + %d = %d\r\n", a, b, cal_sum(a, b));    printf("%d + %d = %d\r\n", a, b, func_ptr(a, b));    while(1)    {    }}
这样写大家应该很熟悉吧,我首先定义了一个函数指针func_ptr,接着将我写得cal_sum函数赋值给了函数指针func_ptr 。然后分别打印函数cal_sum的地址,函数指针func_ptr的地址,以及使用cal_sum计算出来的值,和函数值指针func_ptr计算出来的值。
那么结果是啥样呢?
可以发现函数指针func_ptr和cal_sum函数的存储的地址以及他们所计算出来的值是一样的。
比如在上面求两个数和的基础上再求两个数的乘积和差,会是啥样的呢?
代码是这样的
#include "sys.h"#include "led.h"#include "delay.h"#include "usart.h"uint8_t cal_sum(uint8_t a, uint8_t b){    return a + b;}uint8_t cal_sub(uint8_t a, uint8_t b){    return a - b;}uint8_t cal_mul(uint8_t a, uint8_t b){    return a * b;}int main(void){    delay_init();    uart_init(9600);    uint8_t a = 10;    uint8_t b = 8;    /*定义一个函数指针*/    uint8_t (*func_ptr)(uint8_t, uint8_t);    /*将函数名赋值给函数指针*/    func_ptr = cal_sum;    printf("cal_sum_address  =0x%p\r\n", cal_sum);    printf("func_ptr_address =0x%p\r\n", func_ptr);    printf("%d + %d = %d\r\n", a, b, cal_sum(a, b));    printf("%d + %d = %d\r\n\n", a, b, func_ptr(a, b));    /*将函数名赋值给函数指针*/    func_ptr = cal_sub;    printf("cal_sub_address  =0x%p\r\n", cal_sub);    printf("func_ptr_address =0x%p\r\n", func_ptr);    printf("%d - %d = %d\r\n", a, b, cal_sub(a, b));    printf("%d - %d = %d\r\n\n", a, b, func_ptr(a, b));    /*将函数名赋值给函数指针*/    func_ptr = cal_mul;    printf("cal_mul_address  =0x%p\r\n", cal_mul);    printf("func_ptr_address =0x%p\r\n", func_ptr);    printf("%d * %d = %d\r\n", a, b, cal_mul(a, b));    printf("%d * %d = %d\r\n", a, b, func_ptr(a, b));    while(1)    {    }}
截个图看的更清楚一点
串口打印结果:
指向函数的指针被称作是函数指针。通过函数指针,我们可以灵活的调用各种形式相同,但是功能不同的函数这样做大大的增加了代码的灵活程度。
1、typedef 函数指针
我们在定义一个函数指针时常常会这样写
uint8_t (*func_ptr)(void);
比较好理解,但是下面这个就不好理解了
typedef uint8_t (*func_ptr) (void);
是不是看着有点懵,因为一般的typedef是这样用的
typedef  原类型 别名
用法:
#include<stdio.h>typedef unsigned       char uint8_t;typedef unsigned short int  uint16_t;typedef uint8_t zhiguoxin;void main() {    printf(" \n");     printf(" \n\n");         zhiguoxin a =10;    printf("a=%d\n",a);   }
使用nodepad++编译一下
然后在keil中试验
那这样是啥意思呢?
typedef uint8_t (*func_ptr) (void);
这里是把定义了一个别名叫(*func_ptr) (void) 的吗,显然不对,其含义是:
上面的例子定义func_ptr是一个函数指针, 函数类型是不带形参, 返回参数是uint8_t。
要定义的类型是uint8_t (*)(void),没有输入参数,返回值为uint8_t 的函数指针,定义的别名是func_ptr。
在分析这种形式的定义的时候可以这样看:先去掉typedef和别名, 剩下的就是原变量的类型。去掉typedef和func_ptr以后就剩:uint8_t (*)(void)。
2.为啥使用typedef定义函数指针
答:typedef定义的函数指针类型是比较方便和明了的,因为typedef实际上就是定义一个新的数据类型,typedef有这样的一个作用,就可以用它来定义函数指针类型,这个定义的函数指针类型是能够指向返回值是uint8_t的,并且函数的参数是void类型。
这里定义的typedef uint8_t (*func_ptr) (void);;就相当于把uint8_t (*) (void); 定义成了另一个别名 func_ptr了。这个func_ptr就表示了函数指针类型。
注意:这里的uint8_t (*) (void);实际上不存在这样的写法,只是为了方便理解,这样的写法是不允许的,也是错误的!这样的写法并不代表是一个类型!
转自https://zhuanlan.zhihu.com/p/507908778

使用特权

评论回复
沙发
backlugin| | 2022-12-2 19:53 | 只看该作者
指针函数和函数指针有什么区别?

使用特权

评论回复
板凳
wangdezhi| | 2022-12-2 21:16 | 只看该作者
c语言回调函数例子,看了就明白什么是回调函数了

使用特权

评论回复
地板
febgxu| | 2022-12-3 12:23 | 只看该作者
C语言如何声明一个返回函数指针的函数?

使用特权

评论回复
5
mnynt121| | 2022-12-3 14:49 | 只看该作者
c语言的指针和函数学得很糟糕 看到算法就头疼

使用特权

评论回复
6
mmbs| | 2022-12-4 22:43 | 只看该作者
C语言中的回调函数实在搞不懂               

使用特权

评论回复
7
pl202| | 2022-12-6 14:10 | 只看该作者
要用函数直接调用就好啦。为什么还要用指针???

使用特权

评论回复
8
pmp| | 2022-12-10 09:26 | 只看该作者
在C语言中,指向函数的指针怎么调用函数?

使用特权

评论回复
9
plsbackup| | 2022-12-10 11:26 | 只看该作者
c语言中函数指针是什么  举个实例

使用特权

评论回复
10
yangxiaor520| | 2022-12-11 17:39 | 只看该作者
函数指针很实用

使用特权

评论回复
11
Bowclad| | 2022-12-23 17:13 | 只看该作者
函数指针是如何调用函数的呢?

使用特权

评论回复
12
Undshing| | 2022-12-26 11:44 | 只看该作者
Bowclad 发表于 2022-12-23 17:13
函数指针是如何调用函数的呢?

指针指向函数存储的位置

使用特权

评论回复
13
abotomson| | 2023-1-5 12:23 | 只看该作者
函数指针可以同时传递参数和函数吗?

使用特权

评论回复
14
alvpeg| | 2023-1-5 12:33 | 只看该作者
这个只有一个*,是可以传递任意的参数吗?

使用特权

评论回复
15
timfordlare| | 2023-1-5 12:55 | 只看该作者
指针用的不对,可能会造成很大的风险的。

使用特权

评论回复
16
yorkbarney| | 2023-1-5 15:13 | 只看该作者
这个回调函数怎么返回数值?              

使用特权

评论回复
17
zerorobert| | 2023-1-6 13:31 | 只看该作者
这个可以传递相关的函数给另外的函数吗?

使用特权

评论回复
18
mattlincoln|  楼主 | 2023-1-6 15:21 | 只看该作者
是用在功能注册上的吗?              

使用特权

评论回复
19
biechedan| | 2023-1-6 16:50 | 只看该作者
指针确实是最难学习的一部分。              

使用特权

评论回复
20
vivilyly| | 2023-1-6 18:17 | 只看该作者
如何确定相关的函数已经传递成功?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

1119

帖子

2

粉丝