函数指针是指向函数的指针变量。通过函数指针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
|