概念指针就是一个 地址就像咱们家的门牌号。比如您家住在 幸福小区 3 栋 2 单元 501 室这就是您家的地址别人知道这个地址就能找到您家快递员知道这个地址就能把东西送到您家函数指针就是一个保存了 函数地址 的变量就像一张写着 电灯开关在客厅门口左边 的纸条。普通的函数调用您直接走到客厅门口按下开关直接用函数名调用用函数指针调用您先看纸条知道开关在哪里然后走过去按下先通过指针找到函数地址再调用普通函数的定义是这样的返回值类型 函数名(参数类型1, 参数类型2, ...) { // 函数体 }函数指针的声明只需要把 函数名换成 (* 指针名)返回值类型 (*指针名)(参数类型1, 参数类型2, ...);举个最简单的例子// 普通函数没有返回值没有参数 void kai_deng(void) { // 开灯代码 } // 函数指针指向没有返回值、没有参数的函数 void (*dian_qi_zhi_zhen)(void);(*dian_qi_zhi_zhen)这是函数指针的名字*表示这是一个指针怎么用函数指针声明函数指针就像准备一张空白纸条给函数指针赋值把函数的地址写在纸条上通过函数指针调用函数看着纸条找到开关按下// 1. 声明函数指针 void (*dian_qi_zhi_zhen)(void); // 2. 赋值把kai_deng函数的地址给指针 // 注意函数名本身就是函数的地址所以可以直接写 dian_qi_zhi_zhen kai_deng; // 3. 调用两种写法都可以效果完全一样 dian_qi_zhi_zhen(); // 推荐这种简单 (*dian_qi_zhi_zhen)(); // 这种也对更符合指针的概念带参数和返回值的函数指针就像需要输入和输出的电器刚才的例子是最简单的情况没有参数也没有返回值。但很多函数是有参数和返回值的就像微波炉需要输入 加热时间 这个参数完成后会 叮 一声告诉您返回值计算器需要输入两个数字返回计算结果带一个参数的函数指针// 普通函数设置LED亮度参数是亮度值(0-100) void she_zhi_liang_du(int liang_du) { // 设置PWM占空比的代码 } // 函数指针指向没有返回值、有一个int参数的函数 void (*liang_du_zhi_zhen)(int); // 赋值和调用 liang_du_zhi_zhen she_zhi_liang_du; liang_du_zhi_zhen(50); // 把亮度设置为50%带返回值的函数指针// 普通函数读取温度传感器返回温度值 int du_qu_wen_du(void) { // 读取ADC的代码 return 25; // 假设当前温度是25度 } // 函数指针指向返回int、没有参数的函数 int (*wen_du_zhi_zhen)(void); // 赋值和调用 wen_du_zhi_zhen du_qu_wen_du; int dang_qian_wen_du wen_du_zhi_zhen(); // 得到温度值25函数指针数组就像墙上的一排开关函数指针数组就是一个数组里面的每个元素都是一个函数指针就像墙上的一排开关每个开关控制一个不同的电器。// 三个控制LED的函数 void led1_kai(void) { /* 开LED1 */ } void led2_kai(void) { /* 开LED2 */ } void led3_kai(void) { /* 开LED3 */ } // 函数指针数组有3个元素每个都是指向void(void)函数的指针 void (*led_kai_zu_he[3])(void) {led1_kai, led2_kai, led3_kai}; // 使用按下第0个开关开LED1 led_kai_zu_he[0](); // 按下第1个开关开LED2 led_kai_zu_he[1]();这个在 STM32 里特别有用比如您有多个按键每个按键按下执行不同的功能就可以用函数指针数组来实现。STM32 里最常用的回调函数就像 有事您呼我回调函数是函数指针最重要的应用也是 STM32 HAL 库的核心思想。// 这是HAL库内部偷偷定义的一个函数指针变量 // 它的类型是指向返回void、参数是UART_HandleTypeDef*的函数 void (*UART_RxCpltCallback_Ptr)(UART_HandleTypeDef *huart); // 这是HAL_UART_Receive_IT函数内部的简化代码 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { // 1. 配置串口硬件开启接收中断这部分是硬件操作 // ... // 2. 最重要的一步把您写的回调函数地址写进HAL库的小本子里 UART_RxCpltCallback_Ptr HAL_UART_RxCpltCallback; //此时回调函数HAL_UART_RxCpltCallback的地址是UART_RxCpltCallback_Ptr return HAL_OK; }这里就是把您写的HAL_UART_RxCpltCallback函数的地址赋值给了 HAL 库内部的函数指针变量UART_RxCpltCallback_Ptr。快递到了HAL 库上门送快递当串口收到一个字节的数据时硬件会自动触发一个 中断然后 CPU 会跳转到 HAL 库预先写好的中断服务函数里// 这是HAL库内部的串口1中断服务函数您平时也看不到 void USART1_IRQHandler(void) { // 1. 检查是不是接收完成中断 if(串口确实收到数据了) { // 2. 把收到的数据存到您指定的rx_buffer里 // ... // 3. 最重要的一步通过函数指针调用您的回调函数 if(UART_RxCpltCallback_Ptr ! NULL) // 先检查小本子上有没有写地址 { UART_RxCpltCallback_Ptr(huart1); // 按照地址找到您家敲门 } } }这就是整个过程您写的HAL_UART_RxCpltCallback函数从来没有被您自己直接调用过它是被 HAL 库通过函数指针间接调用的。为什么要用函数指针代码更灵活同一个指针可以指向不同的函数在运行时动态决定调用哪个函数比如您有一个遥控器上面有一个 电源 键按一下开电视再按一下关电视用函数指针的话只需要一个按键处理函数根据当前状态切换指针指向的函数代码更简洁用函数指针数组可以代替大量的 if-else 或者 switch-case 语句比如您有 10 个按键每个按键对应一个函数不用写 10 个 if 判断只需要用按键编号作为数组下标直接调用对应的函数实现回调机制这是 STM32 HAL 库的基础让您不用关心底层硬件的细节只需要专注于应用逻辑HAL 库已经帮您处理了所有复杂的硬件操作您只需要写回调函数就行