当前位置: 首页 > news >正文

C语言函数指针

概念

指针就是一个 "地址",就像咱们家的门牌号。比如:

  • 您家住在 "幸福小区 3 栋 2 单元 501 室",这就是您家的地址
  • 别人知道这个地址,就能找到您家
  • 快递员知道这个地址,就能把东西送到您家

函数指针就是一个保存了 "函数地址" 的变量,就像一张写着 "电灯开关在客厅门口左边" 的纸条。

  • 普通的函数调用:您直接走到客厅门口,按下开关(直接用函数名调用)
  • 用函数指针调用:您先看纸条,知道开关在哪里,然后走过去按下(先通过指针找到函数地址,再调用)

普通函数的定义是这样的:

返回值类型 函数名(参数类型1, 参数类型2, ...) { // 函数体 }

函数指针的声明只需要把 "函数名"换成 "(* 指针名)":

返回值类型 (*指针名)(参数类型1, 参数类型2, ...);

举个最简单的例子

// 普通函数:没有返回值,没有参数 void kai_deng(void) { // 开灯代码 } // 函数指针:指向"没有返回值、没有参数"的函数 void (*dian_qi_zhi_zhen)(void);
  • (*dian_qi_zhi_zhen):这是函数指针的名字,*表示这是一个指针

怎么用函数指针?

  1. 声明函数指针(就像准备一张空白纸条)
  2. 给函数指针赋值(把函数的地址写在纸条上)
  3. 通过函数指针调用函数(看着纸条找到开关,按下)
// 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 库通过函数指针间接调用的。

为什么要用函数指针?

  1. 代码更灵活:同一个指针可以指向不同的函数,在运行时动态决定调用哪个函数

    • 比如您有一个遥控器,上面有一个 "电源" 键,按一下开电视,再按一下关电视
    • 用函数指针的话,只需要一个按键处理函数,根据当前状态切换指针指向的函数
  2. 代码更简洁:用函数指针数组可以代替大量的 if-else 或者 switch-case 语句

    • 比如您有 10 个按键,每个按键对应一个函数
    • 不用写 10 个 if 判断,只需要用按键编号作为数组下标,直接调用对应的函数
  3. 实现回调机制:这是 STM32 HAL 库的基础,让您不用关心底层硬件的细节,只需要专注于应用逻辑

    • HAL 库已经帮您处理了所有复杂的硬件操作,您只需要写回调函数就行
http://www.jsqmd.com/news/802824/

相关文章:

  • 别再只盯着快充了!聊聊交流充电桩(慢充)对电池寿命的友好设计
  • 镜像视界核心算法|跨镜轨迹全域跟踪,无感定位精准赋能白皮书方案
  • 陈皮山楂茯苓白芷甘草生姜提取物一款草本固体饮料
  • 企业AD域DNS转发配置踩坑指南:为什么你的转发总是不生效?
  • 手把手教你维修彩虹1503电热毯:从感温线断线到可控硅触发不足的完整排查
  • 3分钟掌握微信聊天记录永久保存:从数据备份到智能分析完全攻略
  • 构建个性化视觉系统的完整实现方案
  • 长期使用Taotoken Token Plan套餐的成本控制观察
  • 教你京东E卡回收全流程,操作超省心! - 团团收购物卡回收
  • 不止于Kali:在Ubuntu、Debian上给COMFAST CF-812AC无线网卡装RTL8812BU驱动的通用教程
  • 为什么高级工程师会说:Cache 是一种抽象,而不是优化
  • 【ElevenLabs Creator计划深度解密】:20年AI语音工程师亲测的5大准入陷阱与3步通关策略
  • 2026年AI编程软件综合推荐 主流工具全面排行
  • VINS-Mono在EUROC数据集上的实战评测:从轨迹精度到运行耗时,我的避坑心得
  • 2025届最火的六大AI学术神器实际效果
  • 国内智能涡街流量计品牌排行及技术发展深度解析 - 仪表人小余
  • 3步实战构建高性能VLC媒体播放器:深度定制与性能优化全攻略
  • Geek Uninstaller 中找不到 Codex的排查方式
  • 购房指南:养老社区自住与投资的双重考量 - 品牌2026
  • 5分钟掌握重庆大学LaTeX毕业论文模板:告别格式困扰的终极解决方案
  • 终极Windows安卓应用安装指南:告别模拟器,拥抱轻量级体验
  • AI Code Mother 项目学习笔记(一):项目总体介绍与学习路线
  • 2026多品类型材厂家推荐:覆盖航空船舶、汽车、文体娱乐等行业 - 品牌2025
  • 2026年AI驱动SCRM实测:微伴助手领衔,金融医疗零售行业实践深度解析 - 行业产品测评专家
  • peaqOS 给机器发了一份穆迪式评级,机器经济缺的最后一块零件被补上了
  • Claude Opus 4.7 API 国内接入实战:中转方案与稳定性优化
  • 3个简单步骤:在Windows上快速安装Android应用的完整指南
  • 网络小白也能看懂的华为交换机升级指南:用TFTP和Web界面搞定S5700固件更新
  • 想咨询企业微信问题,2026服务商联系方式一键查询 - 品牌2025
  • TrollInstallerX终极指南:iOS 14-16.6.1一键安装TrollStore的完整方案