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

LVGL源码分析(1):lv_ll链表的实现

在LVGL中难免需要用到链表:group中的对象需要用链表来存储,这样可以切换对象的焦点;再比如LVGL内部的定时器,多个定时器也是用链表进行存储的。这篇文章就来分析一下LVGL中链表的源码。

文章目录

  • 1 链表结构体
  • 2 插入元素源码分析
    • 2.1 初始化函数
    • 2.2 插入元素
    • 2.3 插入元素的用法
  • 3 总结

1 链表结构体

对于链表来说,肯定有一个头指针和一个尾指针,在LVGL中,链表的数据结构如下:

/** Dummy type to make handling easier*/
typedef uint8_t lv_ll_node_t;/** Description of a linked list*/
typedef struct {uint32_t n_size;lv_ll_node_t * head;lv_ll_node_t * tail;
} lv_ll_t;

可以看出头尾指针实际上是用一个uint8_t *的指针来保存某个数据的地址。

2 插入元素源码分析

下面以向链表的尾部插入元素为例,来分析一下源码:

2.1 初始化函数

void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size)
{ll_p->head = NULL;ll_p->tail = NULL;/*Round the size up to 4*/node_size = (node_size + 3) & (~0x3);ll_p->n_size = node_size;
}

初始化函数就是初始化一下链表中单个node的大小,这里还将长度四字节对齐了。

2.2 插入元素

_lv_ll_ins_head用于在链表的最前面插入节点,而_lv_ll_ins_tail用于在链表的最后插入节点。它们的实现基本上一样,这里以_lv_ll_ins_tail为例进行分析。

#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t *) + sizeof(lv_ll_node_t *))void * _lv_ll_ins_tail(lv_ll_t * ll_p)
{lv_ll_node_t * n_new;n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE);if(n_new != NULL) {node_set_next(ll_p, n_new, NULL);       /*No next after the new tail*/node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is the old tail*/if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/node_set_next(ll_p, ll_p->tail, n_new);}ll_p->tail = n_new;      /*Set the new tail in the dsc.*/if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/ll_p->head = n_new;}}return n_new;
}

首先就是分配一个大小为ll_p->n_size + LL_NODE_META_SIZE大小的内存,也就是刚刚我们设置的每个节点的大小,然后再加上两个用于保存前一个元素和后一个元素的指针。

然后以node_set_prev函数为例,看下代码做了什么事:

#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev)
{if(act == NULL) return; /*Can't set the prev node of `NULL`*/uint8_t * act8 = (uint8_t *)act;act8 += LL_PREV_P_OFFSET(ll_p);lv_ll_node_t ** act_node_p = (lv_ll_node_t **) act8;lv_ll_node_t ** prev_node_p = (lv_ll_node_t **) &prev;*act_node_p = *prev_node_p;
}

首先 act8 += LL_PREV_P_OFFSET(ll_p)实际上就是actprev指针的位置,然后将这个指针指向的值赋值为参数中的prev指针。对于node_set_next来说,完成的操作类似,就是更改actnext指针的值。

对于这边使用二维指针,把指针的地址取出来然后再给地址里指向的指针赋值,我觉得完全是多此一举,只需要强制转化act8的类型大小为指针的大小(prev元素的类型)即可,这样不会覆盖掉下一个元素的值。这里可能是为了处理更一般化的情况,比如prev不只是一个指针,可能是一个结构体,结构体里有更多信息,但也不保存结构体的地址,而是保存结构体数据,但这种想法似乎也没有什么意义。

所以对于下面这两行的代码来说,就是把新创建节点的prev指向当前链表的最后一个元素,将next指向NULL,这样就在链表的最后插入了一个元素。

node_set_next(ll_p, n_new, NULL);
node_set_prev(ll_p, n_new, ll_p->tail); 

继续分析代码:

if(ll_p->tail != NULL) {                /*If there is old tail then the new comes after it*/node_set_next(ll_p, ll_p->tail, n_new);
}ll_p->tail = n_new;      /*Set the new tail in the dsc.*/
if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/ll_p->head = n_new;
}

这表示对于ll_p结构来说,我们知道前面只保存了单个元素的大小,还有头尾指针。所以最开始先判断当前的链表的尾指针是否有值,若有,则将其next指向我们新创建的节点。然后将链表中的尾指针赋值为新节点的地址。如果链表的头也为空的话,表示链表刚刚创建,该节点不仅是头节点也是尾结点。

2.3 插入元素的用法

有了上面插入元素到链表尾部源码的分析,我们来看看实际上是怎么使用_lv_ll_ins_tail函数的。

lv_obj_t ** next = _lv_ll_ins_tail(&ll);
LV_ASSERT_MALLOC(next);
*next = next_node;

前面源码中我们知道,插入的新元素的内存是在_lv_ll_ins_tail中分配的,所以我们先插入,然后判断如果这个内存分配成功的话,我们就可以把插入到末尾的指针的值赋值为我们的节点next_node

3 总结

实际上LVGL中链表的实现和我们预期的链表数据结构差不多,唯一的不同是这里允许自定义每个节点的大小,然后直接在节点中保存数据,而不是保存指针,这也是一种思路吧。当然,链表的操作不止在尾部插入元素,在lv_ll.c文件中还有获取链表长度、删除节点等函数,如果全部都分析一遍,篇幅就太长了,也是大家熟知的链表,故没有多大的意义。这篇文章的目的就是了解一下LVGL中链表的数据结构,然后以往尾部插入元素为例加深对LVGL中实现的链表的理解。

相关文章:

LVGL源码分析(1):lv_ll链表的实现

在LVGL中难免需要用到链表:group中的对象需要用链表来存储,这样可以切换对象的焦点;再比如LVGL内部的定时器,多个定时器也是用链表进行存储的。这篇文章就来分析一下LVGL中链表的源码。 文章目录 1 链表结构体2 插入元素源码分析…...

js判断数据类型的几种方法及其局限性(typeof, instanceof, Object.prototype.toString.call())

​ js中判断了类型的方法有很多, 这篇文章主要来说一下常用的几种判断类型的方法,以及使用: 每个方法都各有优缺点,在日常使用的时候请结合这些优缺点进行斟酌: 1. 使用typeof判断数据类型 javaScript中typeof可以判断以下类型: undefined: 未定义的变量或者值 boolean: 布…...

【MySQL】一文带你掌握聚合查询和联合查询

文章目录 1. 聚合函数1.1 COUNT1.2 SUM1.3 AVG1.4 MAX,MIN 2. GROUP BY3. HAVING4. 联合查询4.1 内连接4.2 外连接4.3 自连接4.4 子连接 5.合并查询5.1 UNION5.2 UNION ALL 1. 聚合函数 概念: 聚合函数是一种用于处理数据集合的函数,它将多个…...

初步了解JVM

JVM 整体组成部分 类加载器 类加载过程 加载:使用IO读取字节码文件,转换并存储,为每个类创建一个Class对象,存储在方法区中 链接(验证,准备,解析) ​ 验证:对字节码文件格式进…...

嘀嗒陪诊小程序v1.0.8+小程序前端

嘀嗒陪诊小程序功能相对简单,后台也简捷,如果只是做个陪诊服务的小程序也基本能满足了,整体测试了下海参崴发现BUG,小程序端也能正常为使用,唯一用户授权接口是老的。 应用背景:人口老龄化少子化&#xff…...

Java中线程的生命周期

Java中线程的生命周期 Java中线程的声明周期与os中线程的生命周期不太一样,java中线程有6个状态,见下: NEW: 初始状态,线程被创建出来但没有被调用 start() 。RUNNABLE: 运行状态,线程被调用了 start()等待运行的状态…...

光线追踪RayTracing,基本原理,判断物体与光线相交

光线的三点假设: 光线按直线传播光线之间不会发生碰撞光线会经过一系列折射反射进入摄像机 可以从摄像机发出光线,推出可逆的光路 上图中,透明球在与相机直连的线条处,需要将折射和反射的着色点结果相加,如果有光源直…...

三十六、数学知识——组合数(递推法 + 预处理法 + 卢卡斯定理 + 分解质因数求解组合数 + 卡特兰数)

组合数算法主要内容 一、基本思路1、组合数基本概念2、递推法——询问次数多 a b 值较小 模处理(%mod)3、预处理阶乘方法——询问次数较多 a b 值很大 模处理(%mod)4、卢卡斯定理——询问次数较少 (a b 值很大&am…...

LinuxC编程——高级文件操作

目录 一、查询文件信息1、stat2、stat fstat lstat区别 二、目录操作2.1 opendir2.2 readdir2.3 closedir例练习:实现ls操作 三、库3.1 库的定义3.2 库的分类3.2.1 静态库3.2.2 动态库 3.3 创建库3.3.1 静态库制作3.3.2 动态库制作 一、查询文件信息 1、stat int …...

【基础知识整理】图的基本概念 邻接矩阵 邻接表

一、图概述 定义: 图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的; 其中,点通常被成为"顶点(vertex)“,而点与点之间的连线则被成为"边或弧”(edege)。 通常记为,G(V,E)。 图是一种重要的…...

5.程序控制结构|Java学习笔记

文章目录 程序流程控制介绍顺序控制分支控制分支控制if elseswitch分支结构 循环控制for循环控制while循环控制do...while循环控制跳转控制语句breakcontinuereturn 程序流程控制介绍 顺序控制分支控制循环控制 顺序控制 程序从上到下逐行地执行,中间没有任何判断…...

【最优PID 整定】PID性能指标(ISE,IAE,ITSE和ITAE)优化、稳定性裕量(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Linux内核中断和Linux内核定时器

目录 Linux内核中断 Linux内核定时器 Linux内核中断 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) 功能:注册中断 参数: irq : 软中断号 gpio的软中断号 软中断号 gpio_to_i…...

OMG--IDL(Interface Definition Language)

OMG--IDL(Interface Definition Language) 1 概述2 内容缩写IDL 语法和语义概述词法约定ISO Latin-1的字母字符如下表十进制数字字符图形字符格式化字符Tokens注释标识符冲突规则转义标识符关键字IDL识别的其他字符字面量 预处理IDL 语法构建块核心数据类…...

英语学习:M开头

machine 机器 mad 发疯的,生气的 madam 女士,夫人 madame 夫人 magazine 杂志 magic 有魔力的 maid 女仆,侍女 mail 邮递 mailbox 邮箱 mainland 大陆 major 较大的,主要的 majority 大多数 male 雄的 man 人类 man…...

【计算机组成原理与体系结构】控制器

目录 一、CPU的功能与基本结构 二、指令周期的数据流 三、数据通路 四、硬布线控制器 五、微程序控制器 六、微指令 一、CPU的功能与基本结构 运算器基本结构 控制器基本结构 CPU的基本结构 二、指令周期的数据流 取址周期 间址周期 中断周期 指令周期流程 三、数据通路 …...

结构化命令

章节目录: 一、使用 if-then 语句二、if-then-else 语句三、嵌套 if 语句四、test 命令4.1 数值比较4.2 字符串比较4.3 文件比较 五、复合条件测试六、if-then 的高级特性6.1 使用单括号6.2 使用双括号6.3 使用双方括号 七、case 命令八、结束语 本章内容&#xff1…...

Java Web实训项目:西蒙购物网

文章目录 一、创建数据库和表1、创建数据库2、创建用户表3、创建类别表4、创建商品表5、创建订单表 二、创建Simonshop项目1、创建web项目2、修改Artifacts名称:simonshop3、重新部署项目4、编辑首页5、启动应用,查看效果 三、创建实体类1、用户实体类2、…...

ChatGPT Prompt 提示词设计技巧必知必会

本文内容整理自图灵社区直播《朱立成:ChatGPT Prompt提示词技巧必知必会》。 朱立成,图灵社区《ChatGPT即学即用》视频课程作者,软件工程师,对新事物充满好奇,关注ChatGPT应用。2001年毕业于浙江大学,从事软…...

尚硅谷-云尚办公-项目复盘

尚硅谷-云尚办公-项目复盘 资料地址本文介绍问题汇总问题1.knife4j无法下载 视频4问题2.dev等含义 视频5问题3.wrapper继承/实现图 视频8问题4.修改统一返回结果 视频11问题5.修改后新增也变修改 视频29问题6.redis中key值乱码 视频55-60问题7.RangeError: Maximum call stack …...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

微信小程序云开发平台MySQL的连接方式

注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

浅谈不同二分算法的查找情况

二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况&#xf…...

C++ 设计模式 《小明的奶茶加料风波》

👨‍🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

node.js的初步学习

那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...

UE5 音效系统

一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...