函数结构体应用实例
一、函数结构体使用的优点适用场景裸机 / RT‑Thread 嵌入式 C99 开发STM32 项目常用用结构体保存成员数据 函数指针保存操作接口实现 C 语言伪面向对象。1. 数据和行为聚合高内聚普通写法全局变量分散、功能函数独立 结构体把模块状态数据和操作该数据的函数接口打包在一起。相关变量、操作集中管理阅读代码一眼看懂模块有什么数据、能做什么动作。避免大量零散全局变量减少全局命名污染。2. 支持实例化一套代码多个对象最大优势之一结构体可以定义多个实例。 例如一个继电器驱动结构体可以实例化 relay1、relay2共用同一套函数实现只各自保存独立状态。如果用全局变量写驱动要复制多份全局代码大量重复维护灾难。ModuleObj_t dev1; ModuleObj_t dev2; dev1.init(); dev2.init();3. 接口隐藏、封装信息隐藏头文件只暴露结构体前向声明不展开内部成员内部数据私有化外部不能直接访问内部变量只能调用提供的函数指针接口读写外部只能调用对外提供接口禁止直接修改内部状态防止外部乱改成员造成状态异常内部实现改动只要接口函数指针原型不变上层业务代码完全不用修改。4. 多态便于适配不同硬件 / 型号非常适合 STM32 多芯片兼容F407/F030、不同外设硬件适配。相同函数接口原型底层绑定不同实现上层调用完全一致。 举例obj.init drv_f030_init; //F030版本底层实现 //obj.init drv_f407_init;//F407版本底层实现 obj.init();//上层调用代码无需改动可以做到上层业务逻辑一套底层硬件实现按需替换配合条件编译完成多型号适配。5. 降低耦合分层清晰上层业务只操作对象结构体接口不需要依赖底层具体函数名。底层驱动修改、BUG 修复只要接口签名不变业务层代码不用改动。便于模块解耦模块之间只传对象句柄结构体指针不互相引用大量全局。6. 便于单元测试、模拟 mock测试的时候可以把结构体里函数指针替换成模拟 mock 函数不需要真实硬件就可以测试上层业务逻辑。 比如把硬件读写函数指针替换成仿真函数PC 上即可做模块单元测试。二、对应的缺点工程需要权衡有少量内存开销每个实例会占用若干函数指针4 字节 / 个32 位 MCU实例很多时要关注 RAM 占用调试难度略上升函数指针跳转Keil 断点不能直接跳进实现需要看指针指向的真实函数对开发人员要求高错误赋值函数指针会直接跑 HardFault必须严格做参数校验C 语言本身没有访问控制语法“私有” 依靠编码约定强制前向声明才能真正禁止外部访问结构体内部。三、对比普通全局函数 全局变量 vs 结构体函数指针表格维度全局变量 普通函数结构体 函数指针多实例需要复制多份全局重复代码直接定义多个结构体实例封装性外部可随意修改全局变量可隐藏内部成员仅接口访问耦合度业务层硬编码调用底层函数名依赖抽象接口底层可替换多态适配大量 #ifdef 分散业务代码同一套上层底层替换函数指针内存占用全局永久占用 RAM实例按需分配多几个指针开销调试直接断点函数指针间接跳转调试稍麻烦四、应用实例led灯控制初始化、亮、灭、翻转、短暂闪烁、获取当前状态。#ifndef _DEV_LEDRUN_H #define _DEV_LEDRUN_H //----------------------------------------------- // 枚举定义 //----------------------------------------------- typedef enum { LED_RUN_OFF 0, // 灯灭 LED_RUN_ON 1, // 灯亮 } LedRun_StateEnum; //----------------------------------------------- // 接口结构体 //----------------------------------------------- typedef struct { void (*Init)(void); // 初始化 void (*SetOn)(void); // 亮 void (*SetOff)(void); // 灭 void (*Toggle)(void); // 翻转 void (*Blink_Short)(uint8_t times); // 短暂闪烁可设次数 LedRun_StateEnum (*GetState)(void); // 获取当前状态 } LedRun_OpTypeDef; typedef struct { LedRun_StateEnum currentState; const LedRun_OpTypeDef op; } LedRun_DeviceTypeDef; extern LedRun_DeviceTypeDef dev_LedRun; #endif//---------------------------------------------------------------------- //描述: 运行指示灯接口配置与使用源文件 //---------------------------------------------------------------------- #include Dev_LedRun.h //----------------------------------------------- // 运行灯引脚定义 //----------------------------------------------- #define LED_RUN_PORT GPIOC #define LED_RUN_PIN GPIO_Pin_15 #define LED_RUN_CLK RCC_AHBPeriph_GPIOC //----------------------------------------------- // 内部函数声明 //----------------------------------------------- static void LedRun_Init(void); static void LedRun_SetOn(void); static void LedRun_SetOff(void); static void LedRun_Toggle(void); static void LedRun_Blink_Short(uint8_t times); static LedRun_StateEnum LedRun_GetState(void); //----------------------------------------------- // 对外设备实例C99 //----------------------------------------------- LedRun_DeviceTypeDef dev_LedRun { .currentState LED_RUN_OFF, .op { .Init LedRun_Init, .SetOn LedRun_SetOn, .SetOff LedRun_SetOff, .Toggle LedRun_Toggle, .Blink_Short LedRun_Blink_Short, .GetState LedRun_GetState, } }; //----------------------------------------------- // 函数实现 //----------------------------------------------- //---------------------------------------------------------------------- //函数功能: 运行灯初始化 //参数: 无 //返回值: 无 //备注: PB10推挽输出默认灯灭 //---------------------------------------------------------------------- static void LedRun_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_AHBPeriphClockCmd(LED_RUN_CLK, ENABLE); GPIO_InitStruct.GPIO_Pin LED_RUN_PIN; GPIO_InitStruct.GPIO_Mode GPIO_Mode_OUT; GPIO_InitStruct.GPIO_OType GPIO_OType_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(LED_RUN_PORT, GPIO_InitStruct); LedRun_SetOff(); } //---------------------------------------------------------------------- //函数功能: 灯常亮低电平点亮 //---------------------------------------------------------------------- static void LedRun_SetOn(void) { GPIO_WriteBit(LED_RUN_PORT, LED_RUN_PIN, Bit_RESET); dev_LedRun.currentState LED_RUN_ON; } //---------------------------------------------------------------------- //函数功能: 灯常灭高电平熄灭 //---------------------------------------------------------------------- static void LedRun_SetOff(void) { GPIO_WriteBit(LED_RUN_PORT, LED_RUN_PIN, Bit_SET); dev_LedRun.currentState LED_RUN_OFF; } //---------------------------------------------------------------------- //函数功能: 电平翻转 //---------------------------------------------------------------------- static void LedRun_Toggle(void) { if(dev_LedRun.currentState LED_RUN_ON) LedRun_SetOff(); else LedRun_SetOn(); } //---------------------------------------------------------------------- //函数功能: 短暂闪烁可设置次数 //参数: times: 闪烁次数 //---------------------------------------------------------------------- static void LedRun_Blink_Short(uint8_t times) { uint8_t i; if(times 0) return; for(i 0; i times; i) { LedRun_SetOn(); delay_ms(60); LedRun_SetOff(); delay_ms(60); } } //---------------------------------------------------------------------- //函数功能: 获取当前灯状态 //---------------------------------------------------------------------- static LedRun_StateEnum LedRun_GetState(void) { return dev_LedRun.currentState; }

相关新闻

宝宝餐后常喊肚子疼?这背后的原因你可能没想到

宝宝餐后常喊肚子疼?这背后的原因你可能没想到

宝宝餐后常喊肚子疼?这背后的原因你可能没想到最近一两个月,宝宝经常在饭后出现腹部绞痛的情况,已经发生了三次左右。每次持续约三分钟,并伴有轻微腹泻。我在他痛的时候尝试揉肚角和肚脐,但他痛得不让我动他。后来听其…

2026/10/12 14:55:07 阅读更多 →
Vim代码补全实战:从Ctags到LSP,打造高效开发环境

Vim代码补全实战:从Ctags到LSP,打造高效开发环境

1. 项目概述:为什么要在Vim里折腾代码补全? 在Linux环境下,Vim编辑器以其高效、轻量和无处不在的特性,成为无数开发者、系统管理员和极客的首选。然而,对于习惯了现代IDE(如IntelliJ IDEA、PyCharm&#x…

2026/10/10 5:19:52 阅读更多 →
JPEXS 反编译工具从入门到精通的完整实战指南

JPEXS 反编译工具从入门到精通的完整实战指南

JPEXS 反编译工具从入门到精通的完整实战指南 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler JPEXS Free Flash Decompiler(FFDec) 是一款开源免费的 Flash SWF …

2026/10/2 6:40:34 阅读更多 →

最新新闻

Claude-Mem项目解析:本地对话记忆持久化技术探析

Claude-Mem项目解析:本地对话记忆持久化技术探析

项目标题是“claude-mem”,但当前输入中未提供任何有效正文、关键词列表或摘要描述——整段输入为空,仅有一个孤立的标题字符串和空的代码块。根据我的角色设定与创作原则,我必须严格忠于原料:所有内容必须源于用户提供的【项目标…

2026/10/12 14:54:28 阅读更多 →
在线m3u8视频提取下载工具全解析:从HLS分片到MP4转换的实现与避坑指南

在线m3u8视频提取下载工具全解析:从HLS分片到MP4转换的实现与避坑指南

简介:一个在线m3u8视频提取下载工具,基于HLS协议实现,无需安装即可在浏览器中处理分段流媒体。它面向普通用户与前端开发者,解决m3u8列表解析、切片下载及转存MP4格式等问题,适用于离线观看、素材收藏等场景。压缩包共…

2026/10/12 14:54:28 阅读更多 →
Navicat 64bit免安装解压即用版实战:配置迁移与避坑指南

Navicat 64bit免安装解压即用版实战:配置迁移与避坑指南

简介:面向数据库管理员与开发人员的 Navicat 64bit 免安装版资源,压缩包内同时提供 Navicat Premium 新版本与旧版本,解压后即可直接运行,免去繁琐安装流程,便于在多个电脑之间快速部署。该工具支持 MySQL、MariaDB、O…

2026/10/12 14:54:28 阅读更多 →
插件化Java微信机器人:基于WeChatFerry的架构与实战

插件化Java微信机器人:基于WeChatFerry的架构与实战

简介:一份基于WeChatFerry-Java-Client打造的插件化Java微信机器人项目,适合有一定Java基础、希望研究微信自动化消息收发与联系人管理的开发者。资源包共108个文件,约2.68MB,核心代码以57个Java源文件为主,覆盖消息收…

2026/10/12 14:54:28 阅读更多 →
Python+OpenCV+ADB实现微信跳一跳辅助:感知决策执行全链路

Python+OpenCV+ADB实现微信跳一跳辅助:感知决策执行全链路

简介:这份资源面向对Python游戏自动化与计算机视觉感兴趣的开发者,围绕微信小游戏「跳一跳」实现一套自动跳跃辅助工具,涉及图像处理、目标识别与模拟点击等技能点,适合具备Python基础、希望动手实践CV与自动化控制的读者。压缩包…

2026/10/12 14:54:27 阅读更多 →
Python工业排产系统:真实产线约束建模与求解实战

Python工业排产系统:真实产线约束建模与求解实战

简介:本资源是面向智能制造领域开发者与高校参赛学生的Python智能排产调度实战方案,聚焦数字化车间场景下的订单分配、设备协同与动态调度等核心问题。压缩包共182个文件,总大小7.21MB,包含74个Jupyter Notebook(.ipyn…

2026/10/12 14:53:27 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →