一、 链表
1.1 单向链表及双向链表
1.1.1 简介
- 链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构,主要分为单向链表和双向链表
- 链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成,每个节点包括存储数据的数据域和存储下一个节点地址的指针域(第n节点的指针域保存第n+1节点的地址)
- 链表最大的作用就是通过节点把离散的数据链接在一起,组成一个表,常规操作包括节点的插入和删除,人为规定了一个根节点,根节点有一个节点计数器,用于统计整条链表的节点个数
- 下图为单向链表图,每个元素都是一个节点,里面包括数据域和指针域
- 下图为单向循环链表,本质上和单向链表一样,只是在最后一个节点的指针域指向了头节点
- 下图为双向(循环)链表图,其中与单向链表的区别就是双向链表的节点中有两个节点指针,分别指向前后两个节点,其他完全一样
1.1.2 单向链表操作
#include<stdio.h>
#include<stdlib.h>
typedef struct student
{
int num;
int score;
char name[20];
struct student *next;
}STU;
void link_creat_head(STU **p_head,STU *p_new)
{
STU *p_mov = *p_head;
if(*p_head == NULL)
{
*p_head = 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));
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;
p_mov = head;
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;
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函数
vListInitialise();
vListInitialiseItem();
vListInsertEnd();
vListInsert();
uxListRemove();
3.1 列表及列表项初始化函数
void vListInitialise( List_t * const pxList )
{
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
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 列表升序及尾项插入函数
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;
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 )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pxContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
四、列表项的插入和删除实验
- 设计三个任务:start_task(创建其他任务)、task1(LED500ms闪烁一次,提示系统正在运行)、task2(调用列表和列表相关API函数)
- 下面是列表项升序插入、删除、末尾插入代码
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define INCLUDE_vTaskSuspend 1
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIO 1
TaskHandle_t start_task_handle;
#define TASK1_STACK_SIZE 128
#define TASK1_PRIO 2
TaskHandle_t task1_handle;
#define TASK2_STACK_SIZE 128
#define TASK2_PRIO 3
TaskHandle_t task2_handle;
List_t TestList;
ListItem_t ListItem1;
ListItem_t ListItem1;
ListItem_t ListItem1;
void task1( void * pvParameters )
{
while(1)
{
LED0_Turn();
vTaskDelay(500);
}
}
void task2( void * pvParameters )
{
vListInitialise(&TestList);
vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem2);
ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
vListInsert(&TestList,&ListItem1);
vListInsert(&TestList,&ListItem2);
vListInsert(&TestList,&ListItem3);
uxListRemove(&ListItem2);
vListInsert(&TestList,&ListItem2);
while(1)
{
vTaskDelay(500);
}
}
void Start_task( void * pvParameters )
{
taskENTER_CRITICAL();
xTaskCreate(task1,
"task1",
TASK1_STACK_SIZE,
NULL,
TASK1_PRIO,
&task1_handle
);
xTaskCreate(task2,
"task2",
TASK2_STACK_SIZE,
NULL,
TASK2_PRIO,
&task2_handle
);
vTaskDelete(start_task_handle);
taskEXIT_CRITICAL();
}
void freertos_demo()
{
xTaskCreate(Start_task,
"Start_task",
START_TASK_STACK_SIZE,
NULL,
START_TASK_PRIO,
&start_task_handle
);
vTaskStartScheduler();
}