LVGL 普通函数指针与结构体函数指针原理

前言

在 LVGL 事件开发中,开发者经常使用 lv_obj_add_event_cb 绑定回调函数。多数初学者无法理解:回调函数如何被控件保存、事件参数从何而来、两种函数指针的区别是什么。本文从零、零基础、标准化讲解整套原理。

一、普通函数指针

1. 定义

不依赖任何结构体、独立存在的函数指针,称为普通函数指针。

LVGL 官方对事件回调统一类型定义:

复制代码
typedef void (*lv_event_cb_t)(lv_event_t * e);

该类型仅规定函数格式,不属于任何结构体,属于标准普通函数指针类型。

2. 实例

用户自定义的回调函数,其地址即为普通函数指针:

void test_cb(lv_event_t * e) { }

3. 特点

  • 独立存在,不归属任何控件或结构体

  • 仅提供函数执行地址,等待被调用

  • 开发者传入 lv_obj_add_event_cb 的回调函数,均为普通函数指针

二、结构体函数指针

1. 定义

将函数指针作为结构体成员进行保存,该指针称为结构体函数指针。

普通函数指针是"裸函数地址",结构体函数指针是"被结构体收纳保存的函数地址"。

2. LVGL 底层真实结构(简化)

LVGL 并不会直接在 lv_obj_t 中存放回调,而是通过独立事件结构体管理:

cs 复制代码
// 事件描述结构体:保存单个回调信息 
typedef struct lv_event_dsc_t 
{ 
    lv_event_cb_t cb; 
    // 结构体函数指针(真正保存回调的位置) 
    uint32_t event_type; 
    // 事件类型 
    void * user_data; 
    // 用户数据 
} 

lv_event_dsc_t; 

// 控件结构体 
typedef struct lv_obj_t 
{ 
    // 控件坐标、样式等基础属性 
    lv_event_dsc_t * event_list; 
    // 控件挂载的所有事件 
} 

lv_obj_t;

3. 特点

  • 必须依托结构体存在,不能独立使用

  • 每一个控件拥有独立的事件结构体,控件之间回调互不干扰

  • 控件能够绑定多个事件,依靠事件结构体链表实现

三、lv_obj_add_event_cb 工作原理

1. 函数作用

lv_obj_add_event_cb 的核心功能:将用户自定义的普通函数指针,赋值绑定到目标控件所属事件结构体的函数指针成员中,实现回调函数与控件事件的关联。

2. 执行流程

复制代码
`lv_obj_add_event_cb(btn, test_cb, LV_EVENT_CLICKED, NULL);`
  1. test_cb:用户提供的普通函数指针

  2. LVGL 为 btn 控件创建/取出对应的 lv_event_dsc_t 事件结构体

  3. 将普通函数指针 test_cb 赋值给事件结构体内部的 cb 成员,完成回调绑定

  4. 最终回调被保存在控件内部,等待事件触发

四、回调参数 lv_event_t *e 的来源(核心难点)

1. 参数来源说明

开发者从未定义、从未创建 lv_event_t 变量,但回调可以正常接收参数。

原因:事件对象由 LVGL 内核自动生成,不属于用户代码。

2. 底层触发逻辑

当按钮被点击时,LVGL 内核会自动执行事件分发逻辑。为方便读者直观理解完整执行流程,下文为教学简化伪代码,仅用于原理演示,并非工程可编译代码,也不会与 LVGL 内核源码冲突。所有变量均为演示用途,真实逻辑由 LVGL 内核私有代码实现。

bash 复制代码
// 教学伪代码:仅用于原理解释,用户代码不能使用、无需定义 
// dsc 为模拟内核临时指针,仅演示逻辑,非用户可用变量 
        
    lv_event_dsc_t *dsc = btn->event_list;             

// 1. 模拟内核:自动创建局部事件对象 

    lv_event_t evt; 

// 2. 模拟内核:自动填充本次点击的所有事件信息 
// 3. 模拟内核:通过结构体函数指针调用用户回调     

    dsc->cb(&evt);

3. 参数传递关系

  • 内核局部变量 evt:系统自动生成

  • &evt:系统自动传递给回调函数

  • 用户回调形参 lv_event_t *e:接收系统传入的地址

4. 变量名无关性

参数传递仅传递内存地址,与变量名称无关:

  • 内核内部事件变量名称由官方源码固定定义,不可修改;

  • 仅用户侧回调形参名称可自定义,修改为 eevevent 均不影响程序运行;

五、最终核心总结

1. 普通函数指针:用户编写的回调函数,独立存在,无归属。

2. 结构体函数指针:存放在控件事件结构体内部的指针,用于保存回调。

3. 绑定原理:API 将用户的普通函数指针,保存进控件的结构体函数指针中。

4. 参数原理:事件触发时,LVGL 内核在自身私有作用域中自动创建事件对象、完成参数传递,用户仅需通过回调形参接收事件数据即可。

相关推荐
byte轻骑兵1 小时前
【BlueZ 】Adapter 模块:蓝牙适配器(主设备)的初始化与核心逻辑
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
今夕资源网1 小时前
Windows 本地 HTTPS 配置教程:使用 CertTool 创建自签名证书并部署到宝塔
windows·网络协议·https
流浪0011 小时前
Linux系统篇27——通信(四):System V 消息队列 + 信号量:管道之后,Linux 还藏了哪两样 IPC
linux·运维
Java后端的Ai之路1 小时前
23、Python - 策略模式
linux·python·策略模式
tangwangbi11 小时前
Linux 系统配置文件:/etc/profile、~/.bashrc 和 ~/.bash_profile 三者之间的区别与作用
linux·运维·bash
Akiyama_Mio-Kon11 小时前
2026 最新:Dify 本地 Docker 部署完整教程(Windows + Git Bash)
windows·git·docker
蜀道山老天师11 小时前
Shell Bash变量与运算符(含条件测试与流程控制)
linux·运维·bash
Mr.朱鹏11 小时前
Linux 服务器 LVM 根分区在线动态扩容
linux·服务器·数据库
mengge.cloud12 小时前
存储技术基础小白教程
linux·运维·服务器·wpf·存储