简述

函数指针 指向函数的指针变量。通过函数指针C语言可以实现各种强大的功能与设计方法。回调函数是函数指针最常见的用途,是C语言的重中之重,C语言面试当中的必考知识点和难点。

回调函数​ 作为参数传递给另一个函数的函数​。接受回调作为参数的函数预计会在某个时间点执行它。

回调函数 ​机制 允许下层软件层调用上层软件层定义的函数​。开发STM32或者其它单片机时,会用到原厂提供的固件库函数,固件库函数中有非常多回调函数​。如下:

用户层代码

Callback_Register(Callback)

{

/*开始调用,并执行*/

}

驱动层代码

Callback_function​A

Callback_function​B

Callback_function​C

硬件驱动程序​ 是一个独立、可重复使用的程序,硬件驱动程序并不需要了解用户应用程序执行过程与结果。硬件驱动程序只提供API函数,允许用户应用程序将函数注册为回调。回调函数由硬件驱动程序作为执行的一部分进行调用。如果不使用回调,就会被编码为直接调用。直接调用将降低可重复使用性。回调机制的另一个好处是,在程序执行期间可以动态更改被调用的回调函数。


一、函数指针

它就是一个指针,只不过它是一个函数指针,所以指向的是一个函数。实质上与变量指针、指针变量都是一个变量,这个变量存放的是一个地址,在32位单片机中,任何类型的指针变量都存放的是一个大小为4字节的地址。

int   a;      (左边走义变量a,右边定义函数cal_sum)     void cal_sum(void);

int * p;     (左边定义int指针,右边定义func_ptr)         void (*func_ptr)(void);

p=&a;      (左边赋值指针,右边赋值函数指针)            func_ptr= &cal_sum;

keil例程(单片机)

#include "sys.h" /* 系统 */
#include "led.h" /* LED灯 */
#include "delay.h" /* 延时 */
#include "usart.h" /* 串口 */

uint8_t cal_sum(uint8_t a, uint8_t b) /* 声明函数,传入参数为a,b */
{
return a + b; /* 函数返还值为a+b */
}

int main(void) /* 主函数 */
{
delay_init(); /* 延时初始化 */
uart_init(9600); /* 设置串口波特率 */

printf("Hello, World!\r\n"); /* 打印 Hello, World! */
printf("单片机C语言\r\n"); /* 打印 单片机C语言 */

uint8_t a = 8 b = 6; /* 定义变量a,并赋值6 定义变量b,并赋值8*/

uint8_t (*func_ptr)(uint8_t, uint8_t); /* 定义一个函数指针 */

func_ptr = cal_sum; /* 将函数cal_sum赋值给函数func_ptr */
printf("cal_sum_address =0x%p\r\n", cal_sum); /* 打印函数cal_sum地址 */
printf("func_ptr_address =0x%p\r\n", func_ptr); /* 打印指针func_ptr变量(指针变量为地址) */
printf("%d + %d = %d\r\n", a, b, cal_sum(a, b)); /* 打印变量a值,变量b值,函数cal_sum值 */
printf("%d + %d = %d\r\n", a, b, func_ptr(a, b)); /* 打印变量a值,变量b值,函数func_pte值 */

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" /* LED灯 */
#include "delay.h" /* 延时 */
#include "usart.h" /* 串口 */

uint8_t cal_sum(uint8_t a, uint8_t b) /* 声明函数,传入参数为a,b */
{
return a + b; /* 函数返还值为a+b */
}

uint8_t cal_sub(uint8_t a, uint8_t b) /* 声明函数,传入参数为a,b */
{
return a - b; /* 函数返还值为a-b */
}

uint8_t cal_mul(uint8_t a, uint8_t b) /* 声明函数,传入参数为a,b */
{
return a * b; /* 函数返还值为a*b */
}

int main(void) /* 主函数 */
{
delay_init(); /* 延时初始化 */
uart_init(9600); /* 设置串口波特率 */

printf("Hello, World!\r\n"); /* 打印 Hello, World! */
printf("单片机C语言\r\n"); /* 打印 单片机C语言 */

uint8_t a = 8 b = 6; /* 定义变量a,并赋值6 定义变量b,并赋值8*/

uint8_t (*func_ptr)(uint8_t, uint8_t); /* 定义一个函数指针 */

func_ptr = cal_sum; /* 将函数cal_sum赋值给函数func_ptr */
printf("cal_sum_address =0x%p\r\n", cal_sum); /* 打印函数cal_sum地址 */
printf("func_ptr_address =0x%p\r\n", func_ptr); /* 打印指针func_ptr变量(指针变量为地址) */
printf("%d + %d = %d\r\n", a, b, cal_sum(a, b)); /* 打印变量a值,变量b值,函数cal_sum值 */
printf("%d + %d = %d\r\n", a, b, func_ptr(a, b)); /* 打印变量a值,变量b值,函数func_pte值 */

func_ptr = cal_sub; /* 将函数cal_sub赋值给函数func_ptr */
printf("cal_su_address =0x%p\r\n", cal_sub); /* 打印函数cal_sub地址 */
printf("func_ptr_address =0x%p\r\n", func_ptr); /* 打印指针func_ptr变量(指针变量为地址) */
printf("%d + %d = %d\r\n", a, b, cal_sub(a, b)); /* 打印变量a值,变量b值,函数cal_sub值 */
printf("%d + %d = %d\r\n", a, b, func_ptr(a, b)); /* 打印变量a值,变量b值,函数func_pte值 */

func_ptr = cal_mul; /* 将函数cal_sum赋值给函数func_ptr */
printf("cal_mul_address =0x%p\r\n", cal_mul); /* 打印函数cal_mul地址 */
printf("func_ptr_address =0x%p\r\n", func_ptr); /* 打印指针func_ptr变量(指针变量为地址) */
printf("%d + %d = %d\r\n", a, b, cal_mul(a, b)); /* 打印变量a值,变量b值,函数cal_mul值 */
printf("%d + %d = %d\r\n", a, b, func_ptr(a, b)); /* 打印变量a值,变量b值,函数func_pte值 */

while(1) /* 循环 */
{
}
}

通过函数指针可以灵活的调用各种形式相同,但是功能不同的函数这样做大大的增加了代码的灵活程度。


1、typedef 函数指针(同义字)

在定义一个函数指针时

uint8_t (*func_ptr)(void);   这样好理解

typedef uint8_t (*func_ptr) (void);