FreeRTOS-列表及列表项

一、 链表

1.1 单向链表及双向链表

1.1.1 简介

  • 链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构,主要分为单向链表双向链表
  • 链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成,每个节点包括存储数据的数据域存储下一个节点地址的指针域(第n节点的指针域保存第n+1节点的地址)
  • 链表最大的作用就是通过节点把离散的数据链接在一起,组成一个表,常规操作包括节点的插入和删除,人为规定了一个根节点,根节点有一个节点计数器,用于统计整条链表的节点个数
  • 下图为单向链表图,每个元素都是一个节点,里面包括数据域指针域

在这里插入图片描述

  • 下图为单向循环链表,本质上和单向链表一样,只是在最后一个节点的指针域指向了头节点

在这里插入图片描述

  • 下图为双向(循环)链表图,其中与单向链表的区别就是双向链表的节点中有两个节点指针,分别指向前后两个节点,其他完全一样

在这里插入图片描述

1.1.2 单向链表操作

  • 单向链表的创建
#include<stdio.h>
#include<stdlib.h> 

//定义一个名为student的结构体,使用关键字typedef重命名数据类型 
typedef struct student
{
	//数据域 
	int num;
	int score;
	char name[20];
	//指针域,指向下一个节点 
	struct student *next;
}STU;

void link_creat_head(STU **p_head,STU *p_new)		//p_head指向链表头指针的指针 *p_head指向链表头的指针  p_new指向新节点的指针 
{
    STU *p_mov = *p_head;			//p_mov为头节点 
    
    if(*p_head == NULL)				//当第一次加入链表为空时,指向链表头指针空 
    {
        *p_head = p_new;			//p_new是一个结构体指针,作为头节点  P_new指向的地址给头节点 
        p_new->next=NULL;			//表示是当前列表的最后一个节点 
    }
    else //第二次及以后加入链表
    {
        while(p_mov->next!=NULL)	//当头指针遍历找到最后一个节点 
        {
            p_mov=p_mov->next;		//地址移位,一直到最后一个 
        }
 
        p_mov->next = p_new;		//将新申请的节点加入链表
        p_new->next = NULL;			//表示是列表的最后一个节点 
    }
}

int main()
{
    STU *head = NULL,*p_new = NULL;	//链表头指针,新节点指针初始化 
    int num,i;
    printf("请输入链表初始个数:\n");
    scanf("%d",&num);
    
    for(i = 0; i < num;i++)
    {
        p_new = (STU*)malloc(sizeof(STU));//p_new是指向新分配节点的指针,动态分配内存,大小为STU结构体的大小 
        printf("请输入学号、分数、名字:\n"); //给新节点赋值
        scanf("%d %d %s",&p_new->num,&p_new->score,p_new->name);
        link_creat_head(&head,p_new);	//将新节点加入链表
    }
}
  • 单向链表的遍历
//单向链表遍历
void link_print(STU *head)
{
    STU *p_mov;
    //定义新的指针保存链表的首地址,防止使用head改变原本链表
    p_mov = head;
    //当指针保存最后一个结点的指针域为NULL时,循环结束
    while(p_mov!=NULL)
    {
        //先打印当前指针保存结点的指针域
        printf("num=%d score=%d name:%s\n",p_mov->num,\
               p_mov->score,p_mov->name);
 
        //指针后移,保存下一个结点的地址
        p_mov = p_mov->next;
    }
}

//主函数创建后,调用函数,实参放入头节点地址
    link_print(head);

1.2 链表与数组

  • 链表是通过节点离散的数据链接成一个表,地址非连续
  • 数组是通过开辟一段连续的内存来存储数据
  • 数组的每个成员对应着链表中的每个节点,成员和节点可以是基本数据类型或者用户自定义结构体

二、列表及列表项简介

  • 列表是FreeRTOS中的一个数据结构,列表用来跟踪FreeRTOS中的任务列表项是存放在列表中的项目
  • 列表相当于链表列表项相当于节点,FreeRTOS中的列表相当于一个双向(循环)链表
  • 列表的特点:列表间的地址非连续的,列表项的数目是由后期决定,可以改变。数组地址连续,且在最初已经确定了成员数量后期无法改变
  • FreeRTOS中任务数量不确定任务状态会发生变化,适用于列表(链表)数据结构

在这里插入图片描述

  • 下面为列表结构体定义
  • listFIRST_LIST_INTEGRITY_CHECK_VALUE,listSECOND_LIST_INTEGRITY_CHECK_VALUE这两个宏用来判断列表的数据在程序运行过程中,是否遭到破坏
typedef struct xLIST
{
    listFIRST_LIST_INTEGRITY_CHECK_VALUE
    volatile UBaseType_t uxNumberOfItems;//记录列表中列表项的个数,不包含末尾列表项
    ListItem_t * configLIST_VOLATILE pxIndex;//pxIndex用于指向某一个列表项,用于遍历列表项的指针
    MiniListItem_t xListEnd;//末尾列表项
    listSECOND_LIST_INTEGRITY_CHECK_VALUE
} List_t;
  • 列表项是列表中用于存放数据的地方,在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;

  • 迷你列表项:迷你列表项用于标记列表的末尾挂载其他插入列表中的列表项
  • 下面是迷你列表项的结构体定义
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;

三、列表相关API函数

  • 下面为列表相关的API函数
vListInitialise();//初始化列表
vListInitialiseItem();//初始化列表项
vListInsertEnd();//列表末尾插入列表项
vListInsert();//列表插入列表项,数值升序插入,有序的插入到列表中
uxListRemove();//列表移出列表项

3.1 列表及列表项初始化函数

  • 下面为列表初始化API函数
void vListInitialise( List_t * const pxList )//形参:待初始化列表
{
	//初始化时,列表中只有xListEnd,因此pxIndex指向xListEnd
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );//指向某一个成员:结构体成员赋初始值
    //xListEnd的值设置成最大,用于列表项升序排序时,排在最后
    pxList->xListEnd.xItemValue = portMAX_DELAY;//末尾列表项设置成最大
    //初始化时,列表中只有xListEnd
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );//末尾列表项下一个指向自己
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );//末尾列表项上一个指向自己
    //初始化时,列表中的列表项数量为0(不包含xListEnd)
    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//列表项数值设置为0
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );//检查数据完整性
}
  • 下面为列表项初始化API函数
void vListInitialiseItem( ListItem_t * const pxItem )//形参:待初始化列表项
{
	//初始化时,列表项所在列表设为空
    pxItem->pxContainer = NULL;
    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );//检查数据完整性
}

3.2 列表升序及尾项插入函数

  • 下面为列表项升序插入API函数
void vListInsert(List_t * const pxList,ListItem_t * const pxNewListItem)//形参:列表,待插入的列表项
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;//待插入列表项的数值,依据数值升序插入
    listTEST_LIST_INTEGRITY( pxList );//检查参数是否正确
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );//检查参数是否正确
    
    if( xValueOfInsertion == portMAX_DELAY )//数值是否等于最大值
    {
        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 )++;//更新列表项数目
}

  • 下面为列表项尾项插入API函数
  • 此函数用于将待插入列表的列表项插入到列表pxIndex指针指向的列表项前面,是一种无序的插入方法
void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )//形参:目标列表,待插入列表项
{
    ListItem_t * const pxIndex = pxList->pxIndex;//获取列表pxIndex指向的列表项
    listTEST_LIST_INTEGRITY( pxList );
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
    
    pxNewListItem->pxNext = pxIndex;//更新待插入列表项的指针变量成员
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;
    
    mtCOVERAGE_TEST_DELAY();
    
    pxIndex->pxPrevious->pxNext = pxNewListItem;//更新列表中原本列表项的指针成员变量
    pxIndex->pxPrevious = pxNewListItem;
    
    pxNewListItem->pxContainer = pxList;//更新待插入列表项的所在列表成员变量
    ( pxList->uxNumberOfItems )++;//更新列表项数目
}

3.3 列表项的删除函数

  • 下面为列表项删除函数
  • 此函数可以将列表项从所在列表中删除
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )//待移除的列表项
{
    List_t * const pxList = pxItemToRemove->pxContainer;//获取待删除列表项所处列表
    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;//从列表中移出列表项
    if( pxList->pxIndex == pxItemToRemove )//如果pxIndex正指向移除的列表项
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious;//pxIndex就指向上一个列表项
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
    pxItemToRemove->pxContainer = NULL;//将待移除的列表项的所在列表指针清空,表示不属于任何列表
    ( pxList->uxNumberOfItems )--;//列表项数目减1
    return pxList->uxNumberOfItems;//返回列表剩余列表项的数量
}

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

  • 设计三个任务:start_task(创建其他任务)、task1(LED500ms闪烁一次,提示系统正在运行)、task2(调用列表和列表相关API函数)
  • 下面是列表项升序插入、删除、末尾插入代码
#define configSUPPORT_DYNAMIC_ALLOCATION					1//动态创建任务宏定义配置
#define INCLUDE_vTaskSuspend								1//挂起/恢复任务宏定义配置

//start_task参数宏定义
#define	START_TASK_STACK_SIZE 								128//堆栈大小
#define	START_TASK_PRIO										1//任务优先级
TaskHandle_t	start_task_handle;							//任务句柄
//task1参数宏定义
#define	TASK1_STACK_SIZE 									128//堆栈大小
#define	TASK1_PRIO											2//任务优先级
TaskHandle_t	task1_handle;								//任务句柄
//task2参数宏定义
#define	TASK2_STACK_SIZE 									128//堆栈大小
#define	TASK2_PRIO											3//任务优先级
TaskHandle_t	task2_handle;								//任务句柄

List_t 		TestList;//定义列表
ListItem_t 	ListItem1;//定义列表项1
ListItem_t 	ListItem1;//定义列表项2
ListItem_t 	ListItem1;//定义列表项3

//task1任务函数:LED0每500ms闪烁一次
void task1( void * pvParameters )
{
		while(1)
		{
			LED0_Turn();//LED0反转
			vTaskDelay(500);//自带延时函数
		}
}
//task2任务函数:列表项插入和删除
void task2( void * pvParameters )
{
		//初始化
		vListInitialise(&TestList);//初始化列表
		vListInitialiseItem(&ListItem1);//初始化列表项1
		vListInitialiseItem(&ListItem2);//初始化列表项2
		vListInitialiseItem(&ListItem2);//初始化列表项2
		ListItem1.xItemValue = 40;//列表项1数值为40
		ListItem2.xItemValue = 60;//列表项2数值为60
		ListItem3.xItemValue = 50;//列表项3数值为50
		
		vListInsert(&TestList,&ListItem1);//升序插入列表项1
		vListInsert(&TestList,&ListItem2);//升序插入列表项2
		vListInsert(&TestList,&ListItem3);//升序插入列表项3
		//顺序: -> 列表项1 -> 列表项3 -> 列表项2 -> 末尾列表项 ->
		

		uxListRemove(&ListItem2);//移除列表项2
		vListInsert(&TestList,&ListItem2);//末尾插入列表项2
		//顺序同一开始
		
		while(1)
		{
			vTaskDelay(500);//自带延时函数
		}
}
//Start_task任务函数
void Start_task( void * pvParameters )
{
		taskENTER_CRITICAL();//进入临界区
		//创建任务1
		xTaskCreate(task1,//任务函数
					"task1",//任务名称
					TASK1_STACK_SIZE,//堆栈大小/字
					NULL,//入口参数没有
					TASK1_PRIO,//优先级
					&task1_handle//任务句柄
					);
		//创建任务2
		xTaskCreate(task2,//任务函数
					"task2",//任务名称
					TASK2_STACK_SIZE,//堆栈大小/字
					NULL,//入口参数没有
					TASK2_PRIO,//优先级
					&task2_handle//句柄
					);
		vTaskDelete(start_task_handle);
		//删除自身任务Start_task,start_task_handle或NULL都可以
		taskEXIT_CRITICAL();//退出临界区
}

//FreeRTOS入口函数,程序从此处开始执行
void freertos_demo()
{
		xTaskCreate(Start_task,//任务函数
					"Start_task",//任务名称
					START_TASK_STACK_SIZE,//堆栈大小/字
					NULL,//入口参数没有
					START_TASK_PRIO,//优先级
					&start_task_handle//句柄
					);//创建Start任务
		vTaskStartScheduler();//开启任务调度器,开启执行Start任务,创建task123
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值