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

函数结构体应用实例

一、函数结构体使用的优点

适用场景:裸机 / RT‑Thread 嵌入式 C99 开发,STM32 项目常用,用结构体保存成员数据 + 函数指针保存操作接口,实现 C 语言伪面向对象。

1. 数据和行为聚合,高内聚

普通写法:全局变量分散、功能函数独立; 结构体把模块状态数据和操作该数据的函数接口打包在一起。

  • 相关变量、操作集中管理,阅读代码一眼看懂模块有什么数据、能做什么动作。
  • 避免大量零散全局变量,减少全局命名污染。

2. 支持实例化,一套代码多个对象

最大优势之一:结构体可以定义多个实例。 例如一个继电器驱动结构体,可以实例化 relay1、relay2,共用同一套函数实现,只各自保存独立状态。如果用全局变量写驱动,要复制多份全局,代码大量重复,维护灾难。

ModuleObj_t dev1; ModuleObj_t dev2; dev1.init(); dev2.init();

3. 接口隐藏、封装(信息隐藏)

  1. 头文件只暴露结构体前向声明,不展开内部成员,内部数据私有化,外部不能直接访问内部变量,只能调用提供的函数指针接口读写;
  2. 外部只能调用对外提供接口,禁止直接修改内部状态,防止外部乱改成员造成状态异常;
  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 上即可做模块单元测试。

二、对应的缺点(工程需要权衡)

  1. 有少量内存开销:每个实例会占用若干函数指针(4 字节 / 个,32 位 MCU);实例很多时要关注 RAM 占用;
  2. 调试难度略上升:函数指针跳转,Keil 断点不能直接跳进实现,需要看指针指向的真实函数;
  3. 对开发人员要求高,错误赋值函数指针会直接跑 HardFault,必须严格做参数校验;
  4. 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; }
http://www.jsqmd.com/news/1404643/

相关文章:

  • 拟贝叶斯-STFT混合算法在时频分析中的应用与优化
  • Windows 11应用程序控制策略拦截问题解决方案
  • 潍坊冷库聚氨酯保温喷涂哪家靠谱?看懂这三点轻松避开施工坑。 - 滚动商讯
  • HOOPS Visualize Web 快速入门
  • 化工危险实训装备 + 化工预防体验实训装置 + 化工特殊作业实验设备职校产教融合实训室建设方案 - 滚动商讯
  • Python Bad file descriptor错误解析:从文件描述符原理到并发编程实践
  • 【爱马仕】Hermes 桌面智能自动化工具,Windows 整合包无痛部署方案
  • 开源机械臂Moltbot与Mac mini自动化方案:技术拆解与营销迷局
  • 真火壁炉科普:室内烟管越长取暖越好?烟管很短等于浪费热量?
  • 2026 香港新晉訂造傢俬品牌信得過嗎?
  • 北大AI Agent开发教程全解析:从小白到实战的198小时学习路径
  • macOS Python包安装全攻略:从依赖冲突到编译错误的实战解决方案
  • 永久微信活码怎么制作?
  • 外贸GEO02|从SEO到GEO:搜索变了,你的获客方式也要变 - 外贸圈集团
  • 从巴贝奇分析机到现代计算机:通用计算思想的百年传承
  • Typora高效文字高亮与标红:3种方案与实战技巧
  • 2026高端脱模剂推荐,多场景适用
  • CSS控制SVG颜色:从原理到实战的完整指南
  • SQLite FTS全文搜索实战:从倒排索引原理到中文分词优化
  • 2026年南京代账公司靠谱推荐 合规省心的优质选择别错过 - 滚动商讯
  • 8月中下旬出行北京游不同人群怎么选北京旅行社 五家品牌精准匹配 - 滚动商讯
  • 基于Ceph的对象存储网关(S3)在多租户下的性能隔离方案
  • 稻百年胖东来研学|观海码头刘鹏分享服务与管理思考
  • ai免费写论文可行吗?实测3款一键生成论文工具,结果有好有坏!
  • AI Agent记忆系统深度解析:MemSearch与OpenClaw Memory的架构对比与融合实践
  • 湘潭雨湖区新房装修除甲醛怎么选?实地调研对比,本地除醛服务商测评 - 专注室内空气检测治理
  • AD原理图模板路径
  • GitLab CE 团队卡不住 commit 规范?我用一个全局 Hook 把整个公司拦下了
  • # 第三周周记:从组合类型到数据结构,正式踏入“结构化“世界
  • 化工特殊作业实训装备 + 化工应急处置实验装置 + 化工事故警示实验装置园区公共实训中心落地要点 - 滚动商讯