FreeRTOS—列表和列表项

文章目录

  • 一、列表与列表项
    • 1.1.列表与列表项的简介
    • 1.2.列表与列表项相关结构体
      • 1.2.1.列表结构体
      • 1.2.2.列表项结构体
      • 1.2.3.迷你列表项
  • 二、列表相关API函数
    • 2.1.列表相关API函数介绍
      • 2.1.1.`vListInitalise( )`初始化列表函数
      • 2.1.2.`vListInitaliseItem( )`初始化列表项函数
      • 2.1.3.`vListInsertEnd( )`列表末尾插入列表项函数
      • 2.1.4.`vListInsert( )`列表插入列表项函数
      • 2.1.5.`vListRemove( )`列表移除列表项函数
  • 三、列表项的插入和删除实验
    • 3.1.实验设计
    • 3.2.软件设计
      • 3.2.1.列表插入列表项
      • 3.2.2.列表移除列表项
      • 3.2.3.列表末尾插入列表项

一、列表与列表项

1.1.列表与列表项的简介

列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来追踪 FreeRTOS 中的任务,列表项就是存放在列表中的项目;列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表,列表和列表项的关系和下图所示:

在这里插入图片描述

下面这几点说明了使用链表的好处:

  • 列表的特点:列表项间的地址非连续,是人为的连接到一起的,列表项的数目是由后期添加的个数决定的,随时可以改变。
  • 数组的特点:数组成员地址是连续的,数组在最初确定了成员数量后期无法改变。
  • 在 OS 中任务的数量是不确定的,并且任务状态是会发生改变的,所以非常适用列表这种数据结构

1.2.列表与列表项相关结构体

1.2.1.列表结构体

有关列表的均在文件 list.c 和 list.h 中,下面代码是 list.h 文件有关列表相关的结构体:

typedef struct xLIST
{listFIRST_LIST_INTEGRITY_CHECK_VALUE 					/* 校验值 */volatile UBaseType_t 				uxNumberOfItems; 	/* 列表中列表项的数量 */ListItem_t *configLIST_VOLATILE 	pxIndex; 			/* 用于遍历列表 */MiniListItem_t 						xListEnd; 			/* 最后一个列表项 */listSECOND_LIST_INTEGRITY_CHECK_VALUE					/* 校验值 */
} List_t;
  • 在该结构体中,包含了两个宏,这两个宏是确定的已知常量,FreeRTOS 通过检查这两个常量的值,来判断列表的数据在程序运行过程中,是否遭到破坏,该功能一般用于调试,默认是不开启的
  • 成员uxNumberOfItems,用于记录列表中列表项的个数(不包含 xListEnd)
  • 成员pxIndex用于指向列表中的某个列表项,一般用于遍历列表中的所有列表项
  • 成员变量xListEnd是一个迷你列表项,排在最末尾

下图是列表结构示意图:

在这里插入图片描述

1.2.2.列表项结构体

有关列表项的均在文件 list.c 和 list.h 中,下面代码是 list.h 文件有关列表项相关的结构体:

struct xLIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE 				 /* 用于检测列表项的数据完整性 */configLIST_VOLATILE TickType_t 				xItemValue;  /* 列表项的值 */struct xLIST_ITEM * configLIST_VOLATILE 	pxNext; 	 /* 下一个列表项 */struct xLIST_ITEM * configLIST_VOLATILE	 	pxPrevious;  /* 上一个列表项 */void * 										pvOwner; 	 /* 列表项的拥有者 */struct xLIST * configLIST_VOLATILE 			pxContainer; /* 列表项所在列表 */listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE 				 /* 用于检测列表项的数据完整性 */
};
typedef struct xLIST_ITEM ListItem_t;						 /* 重定义成 ListItem_t */
  • 成员变量xItemValue为列表项的值,这个值用于按升序对列表中的列表项进行排序,例如:有任务一,数值是10;任务二,数值是20;任务三,数值是30,添加一个任务四,数值是25,任务四应该加入在任务二和任务三之间
  • 成员变量pxNextpxPrevious分别用于指向列表中列表项的下一个列表项和上一个列表项
  • 成员变量pvOwner用于指向包含列表项的对象(通常是任务控制块)
  • 成员变量pxContainer用于指向列表项所在列表,例如:运行态、就绪态、堵塞态、挂起态

下图是列表项结构示意图:

在这里插入图片描述

1.2.3.迷你列表项

迷你列表项也是列表项,但迷你列表项仅用于标记列表的末尾和挂载其他插入列表中的列表项,用户是用不到迷你列表项的,在 list.h 文件中,有迷你列表项的相关定义,具体的代码所示:

struct xMINI_LIST_ITEM
{listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE				/* 用于检测列表项的数据完整性 */configLIST_VOLATILE TickType_t 			xItemValue; /* 列表项的值 */struct xLIST_ITEM * configLIST_VOLATILE 	pxNext; 	/* 下一个列表项 */struct xLIST_ITEM * configLIST_VOLATILE 	pxPrevious; /* 上一个列表项 */
};
typedef struct xMINI_LIST_ITEM MiniListItem_t;			/* 重定义成 MiniListItem_t */
  • 成员变量xItemValue为列表项的值,这个值多用于按升序对列表中的列表项进行排序。
  • 成员变量pxNextpxPrevious分别用于指向列表中列表项的下一个列表项和上一个列
    表项。
  • 迷你列表项相比于列表项,因为只用于标记列表的末尾挂载其他插入列表中的列表项
    因此不需要成员变量pxOwnerpxContainer,以节省内存开销。

挂载其他插入列表中的列表项:初始化列表的时候总需要一个列表项提供指向上一个和下一个的指针,用于挂载新来的列表项,迷你列表项结构示意图如下图所示:

在这里插入图片描述

二、列表相关API函数

2.1.列表相关API函数介绍

函数描述
vListInitalise( )初始化列表
vListInitaliseItem( )初始化列表项
vListInsertEnd( )列表末尾插入列表项(无序排列)
vListInsert( )列表插入列表项(升序排列)
vListRemove( )列表移除列表项

2.1.1.vListInitalise( )初始化列表函数

下面代码实现的功能是将列表结构体里面的成员赋值,将遍历列表的指针指向最后一个列表项;将列表项的值初始化至最大值为0xFFFFFFFF;将指向上一个和下一个的指针指向最后一个列表项;将列表中的列表项数量赋值为 0:

void vListInitialise(List_t * const pxList)		//参数内容:待初始化列表
{/* 初始化时,列表中只有 xListEnd,因此 pxIndex 指向 xListEnd */pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );/* xListEnd 的值初始化为最大值,用于列表项升序排序时,排在最后 */pxList->xListEnd.xItemValue = portMAX_DELAY;/* 初始化时,列表中只有 xListEnd,因此上一个和下一个列表项都为 xListEnd 本身 */pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*初始化时,列表中的列表项数量为 0(不包含 xListEnd) */pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}

下图是初始化后列表的结构图:

在这里插入图片描述

2.1.2.vListInitaliseItem( )初始化列表项函数

该函数只是把列表项所在列表指针指向空,其他成员都没有改动:

void vListInitialiseItem(ListItem_t * const pxItem)		//参数内容:待初始化列表项
{/* 初始化时,列表项所在列表设为空 */pxItem->pxContainer = NULL;}

下图是初始化后的列表项结构图:

在这里插入图片描述

2.1.3.vListInsertEnd( )列表末尾插入列表项函数

该函数用于新的列表项插入 pxIndex 指针指向的列表项的前面,是一种无序的插入方法,它的参数有两个,第一个是需要插入的列表;第二个是新的列表项:

void vListInsertEnd(List_t * const pxList, ListItem_t * const pxNewListItem)
{/* 获取列表 pxIndex 指向的列表项 */ListItem_t * const pxIndex = pxList->pxIndex;/* 更新待插入列表项的指针成员变量 */pxNewListItem->pxNext = pxIndex;pxNewListItem->pxPrevious = pxIndex->pxPrevious;/* 更新列表中原本列表项的指针成员变量 */pxIndex->pxPrevious->pxNext = pxNewListItem;pxIndex->pxPrevious = pxNewListItem;/* 更新待插入列表项的所在列表成员变量 */pxNewListItem->pxContainer = pxList;/* 更新列表中列表项的数量 */( pxList->uxNumberOfItems )++;
}

下图插入列表项后的列表结构图:

在这里插入图片描述

2.1.4.vListInsert( )列表插入列表项函数

该函数用于新的列表项按升序排列插入列表,它有两个参数,第一个参数是需要插入的列表;第二个是待插入的新列表项:

void vListInsert(List_t * const pxList, ListItem_t * const pxNewListItem)
{//先定义一个指针用于寻找待插入的新列表项的上一个列表项,再定义一个变量用于获取新列表项的值用于比较ListItem_t * pxIterator;const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;/* 如果待插入列表项的值为最大值 */if( xValueOfInsertion == portMAX_DELAY ){/* 插入的位置为列表 xListEnd 前面 */pxIterator = pxList->xListEnd.pxPrevious;}else{/* 遍历列表中的列表项,找到插入的位置 */for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );pxIterator->pxNext->xItemValue <= xValueOfInsertion;pxIterator = pxIterator->pxNext ){}}/* 将待插入的列表项插入指定位置 */pxNewListItem->pxNext = pxIterator->pxNext;pxNewListItem->pxNext->pxPrevious = pxNewListItem;pxNewListItem->pxPrevious = pxIterator;pxIterator->pxNext = pxNewListItem;/* 更新待插入列表项所在列表 */pxNewListItem->pxContainer = pxList;/* 更新列表中列表项的数量 */( pxList->uxNumberOfItems )++;
}

下图是插入列表项后的列表结构图:

在这里插入图片描述

2.1.5.vListRemove( )列表移除列表项函数

该函数用于将列表项所在列表中移除,它的参数是待移除的列表项,返回值是一个整数,表示所在列表剩余的列表项的数量:

UBaseType_t uxListRemove(ListItem_t * const pxItemToRemove)
{List_t * const pxList = pxItemToRemove->pxContainer;/* 从列表中移除列表项 */pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;/* 如果 pxIndex 正指向待移除的列表项 */if( pxList->pxIndex == pxItemToRemove ){/* pxIndex 指向上一个列表项 */pxList->pxIndex = pxItemToRemove->pxPrevious;}else{mtCOVERAGE_TEST_MARKER();}/* 将待移除列表项的所在列表指针清空 */pxItemToRemove->pxContainer = NULL;/* 更新列表中列表项的数量 */( pxList->uxNumberOfItems )--;/* 返回列表项移除后列表中列表项的数量 */return pxList->uxNumberOfItems;
}

下图是移除列表项后的列表结构图:

在这里插入图片描述

三、列表项的插入和删除实验

3.1.实验设计

设计三个任务如下所示:

  • start_task:用来创建下面两个任务
  • task1:实现 LED0 每 500ms 闪烁一次,用来提示系统正在运行
  • task2:调用列表和列表项相关的 API 函数,并且通过串口输出相应信息

3.2.软件设计

在 FreeRTOS 入口函数前面,定义测试列表和三个列表项:

List_t                  TestList;  
ListItem_t              ListItem1; 
ListItem_t              ListItem2; 
ListItem_t              ListItem3; 

并在 task2 函数里初始化列表和三个列表项,给这个三列表项进行赋值:

vListInitialise(&TestList);     
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;

把列表项的地址打印出来,证明列表项之间的准确性。

3.2.1.列表插入列表项

在 task2 函数里面编写所有插入和删除函数,通过下面代码打印,可以知道最后的列表项地址为0cc,列表项 1、2、3 的地址分别是:

  • ListItem1:0d8
  • ListItem2:0ec
  • ListItem3:100
printf("项目\t\t\地址\r\n");
printf("TestList\t\t0x%p\t\r\n", &TestList);
printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
printf("/**************************结束***************************/\r\n");
printf("按下KEY0键继续!\r\n\r\n\r\n");
while (key_scan(0) != KEY0_PRES)
{vTaskDelay(10);
}

在这里插入图片描述

在前面的初始化,task3 的值为 50,应该插入在 task1 和 task2 之间,下面将列表项 1、2、3 简称为 1、2、3;理论上来说,按升序排列,1 是最小的数值,2 是最大的数值,3 排中间,因此 1 的 Next 应该指向 3 的地址,1 的 Previous 应该指向 End (0cc);2 的 Next 应该指向 End (0cc),2 的 Previous 应该指向 3 的地址;而 3 的 Next 应该指向 2 的地址,3 的 Previous 应该指向 1 的地址;最后通过串口输出的数据证明,情况确实如此:

//在列表分别插入列表项1、2、3
vListInsert((List_t*    )&TestList,        (ListItem_t*)&ListItem1); 
vListInsert((List_t*    )&TestList,       (ListItem_t*)&ListItem2);       
vListInsert((List_t*    )&TestList,        (ListItem_t*)&ListItem3);      
printf("项目\t\t\地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
printf("按下KEY0键继续!\r\n\r\n\r\n");
while (key_scan(0) != KEY0_PRES)
{vTaskDelay(10);
}

在这里插入图片描述

3.2.2.列表移除列表项

本次目标是移除列表项 2,只需要证明 1 的 Next 指向 3,1 的 Previous 指向 End(0cc);3 的 Next 指向 End(0cc) ,3 的 Previous 指向 1:

uxListRemove((ListItem_t*   )&ListItem2); 	//需要移除的列表项
printf("项目\t\t\地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
printf("按下KEY0键继续!\r\n\r\n\r\n");
while (key_scan(0) != KEY0_PRES)
{vTaskDelay(10);
}

在这里插入图片描述

3.2.3.列表末尾插入列表项

该函数的设定是将新的列表项,插入到 pxIndex 所指的列表项的前面,由第一个步骤可以发现,指针 pxIndex 指向 End ,因此现在新的列表项将会在 End 前面插入,输出的结果和升序排列插入一样:

vListInsertEnd((List_t*     )&TestList,     //列表(ListItem_t* )&ListItem2);   //需要插入的列表项
printf("项目\t\t\地址\r\n");
printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");

在这里插入图片描述

想要将 2 插入在指定列表项之前,只需修改指针 pxIndex 所指的列表项即可,例如:在列表项 1 前面插入:

TestList.pxIndex = &ListItem1; //pxIndex指向列表项1
vListInsertEnd((List_t*     )&TestList,     //列表(ListItem_t* )&ListItem2);   //需要插入的列表项
printf("项目\t\t\地址\r\n");
printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");

2 的 Next 指向 1 ;2 的 Previous 指向 End:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89389.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89389.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超详细 anji-captcha滑块验证uniapp微信小程序前端组件

由于步骤太多&#xff0c;字数太多&#xff0c;废话也太多&#xff0c;所以前后端分开讲了&#xff0c;后端文章请看&#xff1a; 超详细 anji-captcha滑块验证springbootuniapp微信小程序前后端组合https://blog.csdn.net/new_public/article/details/149116742 anji-captcha…

面向对象编程篇

文章目录一、思维导图二、详细内容第 6 章&#xff1a;面向对象编程基础6.1 面向对象编程的概念和优势6.2 类和对象的定义与创建6.3 类的属性和方法6.4 构造函数&#xff08;__init__&#xff09;和析构函数&#xff08;__del__&#xff09;6.5 封装、继承和多态的实现第 7 章&…

虚拟商品自动化实践:闲鱼订单防漏发与模板化管理的技术解析

最近阿灿发现了一款闲鱼虚拟商品卖家必备神器&#xff01;告别手动发货&#xff0c;订单自动处理&#xff0c;防错防漏&#xff0c;支持课程、激活码、电子书等多种商品&#xff0c;预设模板更省心。文末获取工具&#xff01;最厉害的是&#xff0c;你完全不用一直开着电脑。以…

【Zephyr开发实践系列】08_NVS文件系统调试记录

文章目录前言一、NVS原理介绍&#xff1a;二、BUG-NO1&#xff1a;将NVS运用在NAND-Flash类大容量存储设备2.1 情况描述&#xff1a;2.2 BUG复现&#xff1a;文件系统设备树构建测试应用编写&#xff08;导致错误部分&#xff09;&#xff1a;问题呈现&#xff1a;2.3 问题简述…

网络安全第二次作业

靶场闯关1~8 1. 在url后的name后输入payload ?name<script>alert(1)</script> 2. 尝试在框中输入上一关的payload,发现并没有通过&#xff0c;此时我们可以点开页面的源代码看看我们输入的值被送到什么地方去了 从图中可以看到&#xff0c;我们输入的值被送到i…

LangChain 源码剖析(七)RunnableBindingBase 深度剖析:给 Runnable“穿衣服“ 的装饰器架构

每一篇文章都短小精悍&#xff0c;不啰嗦。一、功能定位&#xff1a;Runnable 的 "增强包装器"RunnableBindingBase 是 LangChain 中实现装饰器模式的核心组件。它就像给原有 Runnable 套上一件 "功能外套"—— 不改变原有 Runnable 的核心逻辑&#xff0c…

为 Git branch 命令添加描述功能

写在最前面的使用方式 查看 所有分支的备注 git branch.notes创建分支并为分支添加备注 git co -b feat/oauth -m 第三方用户登录对分支描述的添加与清除 添加 git branch.note --add 清除 git branch.note --clear &#x1f4dd; 为 Git branch 命令添加描述功能 &#x…

LeetCode|Day18|20. 有效的括号|Python刷题笔记

LeetCode&#xff5c;Day18&#xff5c;20. 有效的括号&#xff5c;Python刷题笔记 &#x1f5d3;️ 本文属于【LeetCode 简单题百日计划】系列 &#x1f449; 点击查看系列总目录 >> &#x1f4cc; 题目简介 题号&#xff1a;20. 有效的括号 难度&#xff1a;简单 题目…

使⽤Pytorch构建⼀个神经⽹络

关于torch.nn:使⽤Pytorch来构建神经⽹络, 主要的⼯具都在torch.nn包中.nn依赖于autograd来定义模型, 并对其⾃动求导.构建神经⽹络的典型流程:定义⼀个拥有可学习参数的神经⽹络遍历训练数据集处理输⼊数据使其流经神经⽹络计算损失值将⽹络参数的梯度进⾏反向传播以⼀定的规则…

网络爬虫的详细知识点

基本介绍 什么是网络爬虫 网络爬虫&#xff08;Web Crawler&#xff09;是一种自动化程序&#xff0c;用于从互联网上抓取、解析和存储网页数据。其核心功能是模拟人类浏览行为&#xff0c;通过HTTP/HTTPS协议访问目标网站&#xff0c;提取文本、链接、图片或其他结构化信息&…

AndroidX中ComponentActivity与原生 Activity 的区别

一、AndroidX 与原生 Activity 的区别 1. 概念与背景 原生 Activity&#xff1a;指 Android 早期&#xff08;API 1 起&#xff09;就存在于 android.app 包下的 Activity 类&#xff08;如 android.app.Activity&#xff09;&#xff0c;是 Android 最初的 Activity 实现&…

Spring AI 使用 Elasticsearch 作为向量数据库

前言 嗨&#xff0c;大家好&#xff0c;我是雪荷&#xff0c;最近在公司开发 AI 知识库&#xff0c;同时学到了一些 AI 开发相关的技术&#xff0c;这期先与大家分享一下如何用 ES 当做向量数据库。 安装ES 第一步我们先安装 Elasticsearch&#xff0c;这里建议 Elasticsear…

TypeScript 配置全解析:tsconfig.json、tsconfig.app.json 与 tsconfig.node.json 的深度指南

前言在现代前端和后端开发中&#xff0c;TypeScript 已经成为许多开发者的首选语言。然而&#xff0c;TypeScript 的配置文件&#xff08;特别是多个配置文件协同工作时&#xff09;常常让开发者感到困惑。本文将深入探讨 tsconfig.json、tsconfig.app.json 和 tsconfig.node.j…

读书笔记(学会说话)

1、一个人只有会说话&#xff0c;才会有好人缘&#xff0c;做事才会顺利。会说话的人容易成功。善于说话的人易成功&#xff0c;而不善说话的人往往寸步难行。我们要把话说得好听&#xff0c;同时更要把事做得漂亮。或许一句话&#xff0c;一件事&#xff0c;就可能使人生的旅途…

私有服务器AI智能体搭建-大模型选择优缺点、扩展性、可开发

以下是主流 AI 框架与模型的对比分析&#xff0c;涵盖其优缺点、扩展性、可开发性等方面。 文章目录一、AI 框架对比二、主流大模型对比三、扩展性对比总结四、可开发性对比总结五、选择建议&#xff08;按场景&#xff09;六、未来趋势一、AI 框架对比 框架优点缺点扩展性可开…

OpenCV直线段检测算法类cv::line_descriptor::LSDDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该类用于实现 LSD (Line Segment Detector) 直线段检测算法。LSD 是一种快速、准确的直线检测方法&#xff0c;能够在不依赖边缘检测的前提下直接从…

Go语言流程控制(if / for)

分支结构package mainimport ("fmt""strconv" )/* 1.顺序结构 2.分支结构 3.循环结构 *//* if 条件1 {// 条件1为真时执行的代码 } else if 条件2 {// 条件1为假但条件2为真时执行的代码 } else {// 所有条件均为假时执行的代码 }一种特殊的条件分支结构if…

wx小程序设置沉浸式导航文字高度问题

第一步&#xff1a;在app.json中设置"navigationStyle": "custom"第二步骤&#xff1a;文件的home.js中// pages/test/test.js Page({/*** 页面的初始数据*/data: {statusBarHeight: 0,navBarHeight: 44 // 自定义导航内容区高度(单位px)},/*** 生命周期函…

C++算法竞赛篇:DevC++ 如何进行debug调试

C算法竞赛篇&#xff1a;DevC 如何进行debug调试前言一、准备工作&#xff1a;编译生成可执行程序二、核心步骤&#xff1a;设置断点与启动调试1. 设置断点2. 启动调试模式三、调试操作&#xff1a;逐步执行与变量监控1. 逐步执行代码2. 监控变量值变化四、调试结束前言 在算法…

语音大模型速览(三)- cosyvoice2

CosyVoice 2: Scalable Streaming Speech Synthesis with Large Language Models 论文链接&#xff1a;https://arxiv.org/pdf/2412.10117代码链接&#xff1a;https://github.com/FunAudioLLM/CosyVoice 一句话总结 CosyVoice 2 是一款改进的流式语音合成模型&#xff0c;其…