#1.链表的定义
链表是一种常见的基础数据结构,结构体指针在这里得到了充分的利用。链表可以动态的进行存储分配,也就是说,链表是一个功能极为强大的数组,他可以在节点中定义多种数据类型,还可以根据需要随意增添,删除,插入节点。链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的。链表中每个节点都分为两部分,一个数据域,一个是指针域。说到这里你应该就明白了,链表就如同车链子一样,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。
作为有强大功能的链表,对他的操作当然有许多,比如:链表的创建,修改,删除,插入,输出,排序,反序,清空链表的元素,求链表的长度等等。
初学链表,一般从单向链表开始
--->NULL
head
这是一个空链表。
---->[p1]---->[p2]...---->[pn]---->[NULL]
head p1->next p2->next pn->next
有n个节点的链表。
创建链表
typedef struct student{
int score;
struct student *next;
} LinkList;
一般创建链表我们都用typedef struct,因为这样定义结构体变量时,我们就可以直接可以用LinkList *a;定义结构体类型变量了。
初始化一个链表,n为链表节点个数。
LinkList *creat(int n){
LinkList *head, *node, *end;//定义头节点,普通节点,尾部节点;
head = (LinkList*)malloc(sizeof(LinkList));//分配地址
end = head; //若是空链表则头尾节点一样
for (int i = 0; i < n; i++) {
node = (LinkList*)malloc(sizeof(LinkList));
scanf("%d", &node->score);
end->next = node;
end = node;
}
end->next = NULL;//结束创建
return head;
#2.FreeRTOS链表操作
首先把我原来发过的FreeRTOS工程建立模板拷贝一份,改名为FreeRTOS-list,将FreeRTOS源码移植进FreeRTOS-list\FreeRTOS\include 路径下。
其次依次添加到工程中,并添加路径。同时创建两个文件分别是list.c保存在FreeRTOS-list\USER\portable路径下和list.h保存在FreeRTOS-list\USER\include路径下。(添加路径)
在这里前期工作我们已经准备好,接下来就要在list.c和list.h中编写程序代码了。首先我们要建立一个链表,我们要在list.h中定义一个结构体。这里一定要引用#include “FreeRTOS.h”,不然会报错。
/* 节点结构体定义 */
struct xLIST_ITEM
{
TickType_t xItemValue;//辅助值,用来帮助节点做顺序排列
struct xLIST_ITEM * pxNext;//指向链表下一个节点
struct xLIST_ITEM * pxPrevious;//指向链表前一个节点
void * pvOwner; //指向拥有该节点的内核对象,通常是TCB
void * pvContainer;//指向该节点所在链表
};
typedef struct xLIST_ITEM ListItem_t; //节点数据类型重定义
接下来我们将对链表进行增删改查,这里我将不在说明具体请看代码。list.h文件中:
#ifndef LIST_H
#define LIST_H
/*
************************************************************************
* 头文件
************************************************************************
*/
#include "FreeRTOS.h"
/*
************************************************************************
* 结构体定义
************************************************************************
*/
/* 节点结构体定义 */
struct xLIST_ITEM
{
TickType_t xItemValue;//辅助值,用来帮助节点做顺序排列
struct xLIST_ITEM * pxNext;//指向链表下一个节点
struct xLIST_ITEM * pxPrevious;//指向链表前一个节点
void * pvOwner; //指向拥有该节点的内核对象,通常是TCB
void * pvContainer;//指向该节点所在链表
};
typedef struct xLIST_ITEM ListItem_t; //节点数据类型重定义
/* mini节点结构体定义,作为双向链表的结尾,因为双向链表是首尾相连的,头和尾一样 */
struct xMINI_LIST_ITEM
{
TickType_t xItemValue;//辅助值,用来帮助节点做顺序排列
struct xLIST_ITEM * pxNext;//指向链表下一个节点
struct xLIST_ITEM * pxPrevious;//指向链表前一个节点
};
typedef struct xMINI_LIST_ITEM MiniListItem_t; //最小节点数据类型重定义
/* 链表结构体定义*/
typedef struct xLIST
{
UBaseType_t uxNumberOfItems; /*链表节点计数器*/
ListItem_t * pxIndex; /*链表节点索引指针 */
MiniListItem_t xListEnd; /* 链表最后一个节点 */
} List_t;
/*
************************************************************************
* ºê¶¨Òå
************************************************************************
*/
/* 初始化节点拥有者 */
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
/* 获取节点拥有者 */
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
/* 初始化节点排序辅助值*/
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
/* 获取节点排序辅助值 */
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
/*获取链表根节点的节点计数器值*/
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
/* 获取链表的入口节点*/
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
/* 获取链表的第一个节点 */
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
/* 获取链表最后一个节点 */
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
/*判断链表是否为空*/
#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )
/*获取链表的节点数*/
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
/* 获取链表的owner即TCB */
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
{ \
List_t * const pxConstList = ( pxList ); \
/* 节点索引指向链表第一个节点调整节点索引指针,指向一个节点,如果当前链表有N个节点,当地N次调用该函数时,pxIndex指向第N个节点*/\
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
/* 链表为空 */ \
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{ \
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
} \
/*获取链表的owner即TCB */ \
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
}
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
/*
************************************************************************
* º¯ÊýÉùÃ÷
************************************************************************
*/
void vListInitialise( List_t * const pxList );
void vListInitialiseItem( ListItem_t * const pxItem );
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem );
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem );
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );
#endif /* LIST_H */
list.c文件中:
#include "FreeRTOS.h"
#include <stdlib.h>
#include "list.h"
/*链表根节点初始化 */
void vListInitialise( List_t * const pxList )
{
/*将链表索引指针指向最后一个节点*/
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
/* 将链表最后一个节点的辅助排序得知设置为最大,确保该节点就是链表最后一个节点*/
pxList->xListEnd.xItemValue = portMAX_DELAY;
/* 将最后一个节点的pxNext和pxPrevious 指针指向自身,为空 */
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
/* 初始化链表节点计数器的值为0,链表为空 */
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
}
/*节点初始化 */
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* 初始化该节点所在链表为空,表示节点还没有插入任何值*/
pxItem->pvContainer = NULL;
}
/*将节点插入到链表尾部 */
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
/* 记住该节点所在的索引*/
pxNewListItem->pvContainer = ( void * ) pxList;
/* 链表节点计数器++ */
( pxList->uxNumberOfItems )++;
}
/* 将节点按升序排列插入到链表 */
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
/* 获取节点排序辅助值 */
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
/* 寻找节点要插入的位置 */
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->pvContainer = ( void * ) pxList;
/* 链表计数器++ */
( pxList->uxNumberOfItems )++;
}
/*将节点从链表删除 */
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
/*获取节点所在链表 */
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
/* Make sure the index is left pointing to a valid item. */
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
/*初始化该节点所在链表为空,表示没有插入任何元素 */
pxItemToRemove->pvContainer = NULL;
/*链表计数器-- */
( pxList->uxNumberOfItems )--;
/*返回链表中剩余节点个数 */
return pxList->uxNumberOfItems;
}
下一章我将对链表删除和插入进行详细解说,以上程序对操作系统链表实现所有功能,可以通过软件调试测出结果。