代码改变世界

freertos 双向循环链表插入删除的实现与直观理解

2022-01-28 22:07  jym蒟蒻  阅读(610)  评论(0编辑  收藏  举报

freertos 双向循环链表插入删除的实现与直观理解

    • main.c
    • list.h
    • 其他头文件
      • FreeRTOS.h
      • FreeRTOSConfig.h
      • portable.h
      • portmacro.h
    • list.c
      • vListInsertEnd函数
      • vListInsert函数
      • uxListRemove函数

 

main.c

用debug之后,查看观察窗口,结果如下。这个实验目的就是,建一个根节点,三个普通节点的链表,并且这三个普通节点按照xitemValue的值进行升序排列。

在这里插入图片描述

main.c里面

首先看节点的类型:链表根节点类型是xLIST、普通节点类型是xLIST_ITEM。

然后看main函数:vListInitialise这个是链表根节点初始化函数、vListInitialiseItem这个是普通节点初始化函数、xItemValue这个是普通节点里面的成员变量、vListInsert这个函数实现将节点插入链表,按照升序排列功能。

接下来分别研究上面这些东西。

#include "list.h"

/* 定义链表根节点 */
struct xLIST       List_Test;

/* 定义节点 */
struct xLIST_ITEM  List_Item1;
struct xLIST_ITEM  List_Item2;
struct xLIST_ITEM  List_Item3;

int main(void)
{	
	
    /* 链表根节点初始化 */
    vListInitialise( &List_Test );
    
    /* 节点1初始化 */
    vListInitialiseItem( &List_Item1 );
    List_Item1.xItemValue = 1;
    
    /* 节点2初始化 */    
    vListInitialiseItem( &List_Item2 );
    List_Item2.xItemValue = 2;
    
    /* 节点3初始化 */
    vListInitialiseItem( &List_Item3 );
    List_Item3.xItemValue = 3;
    
    /* 将节点插入链表,按照升序排列 */
    vListInsert( &List_Test, &List_Item2 );    
    vListInsert( &List_Test, &List_Item1 );
    vListInsert( &List_Test, &List_Item3 );    
    
    for(;;)
	{
		/* 啥事不干 */
	}
}

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次调用该函数时,pxInedex则指向第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 */


其他头文件

FreeRTOS.h

#ifndef INC_FREERTOS_H
#define INC_FREERTOS_H

#include "FreeRTOSConfig.h"
#include "portable.h"

#endif /* INC_FREERTOS_H */

FreeRTOSConfig.h

这个里面由于下一句话,

#define configUSE_16_BIT_TICKS		0

使得portmacro.h里面进行下面的操作,也就是让TickType_t表示32位。

typedef uint32_t TickType_t;
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#define configUSE_16_BIT_TICKS		0

#endif /* FREERTOS_CONFIG_H */

portable.h

#ifndef PORTABLE_H
#define PORTABLE_H

#include "portmacro.h"

#endif /* PORTABLE_H */

portmacro.h

这里面是一些freertos里面的数据类型重定义。

#ifndef PORTMACRO_H
#define PORTMACRO_H

#include "stdint.h"
#include "stddef.h"


/* 数据类型重定义 */
#define portCHAR		char
#define portFLOAT		float
#define portDOUBLE		double
#define portLONG		long
#define portSHORT		short
#define portSTACK_TYPE	uint32_t
#define portBASE_TYPE	long

typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;

#if( configUSE_16_BIT_TICKS == 1 )
	typedef uint16_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffff
#else
	typedef uint32_t TickType_t;
	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif

#endif /* PORTMACRO_H */

list.c

vListInitialise函数进行链表根节点初始化。初始化有什么效果可以看下面这个图。

根节点初始化后,里面的指针全指向了一个地方。

在这里插入图片描述

直白点表述:

在这里插入图片描述

vListInsertEnd函数

vListInsertEnd函数将节点插入到链表的尾部。将一个节点插入后,效果如下。

在这里插入图片描述

用图表示的话:

在这里插入图片描述

更直白点:

在这里插入图片描述

如果按下面这个来编写插入的代码,是错误的。

	pxNewListItem->pxNext = pxIndex;
	pxNewListItem->pxPrevious = pxIndex;
	pxIndex->pxNext = pxNewListItem;
	pxIndex->pxPrevious = pxNewListItem;

试一试就知道了,如果按上面这个代码来,插入三个节点,最后效果如下。这个后果就是,新插上来的节点,他们之间是没有建立联系的,只是和根节点建立了联系。

在这里插入图片描述

下面将三个节点插入,正确的变量变化情况如下:

在这里插入图片描述

这里面就很清楚了,这个是属于循环双向链表。用图表示的话就是:

在这里插入图片描述

如果说新加进来一个item3,在item3没加进来的时候图的表示:

在这里插入图片描述

结合上面的两幅图,编代码。下面这个代码就考虑到了节点之间的联系。

pxNewListItem->pxNext = pxIndex;//上图右边的pxNext连到最左边的pxIndex
pxNewListItem->pxPrevious = pxIndex->pxPrevious;//第二行代码
pxIndex->pxPrevious->pxNext = pxNewListItem;//第三行代码
pxIndex->pxPrevious = pxNewListItem;//第四行代码

第一行代码直观表示如下面绿线:

在这里插入图片描述

pxIndex->pxPrevious一开始指向的是item2,加了item3,那么item3的pxPrevious就得指向item2,也就是pxIndex->pxPrevious。于是有了下面第二行代码。第二行代码直观表示如下。

在这里插入图片描述

第三行代码,pxIndex->pxPrevious->pxNext也就相当于item2的pxNext,加了item3,那么item2的pxNext应该指向item3。第三行代码直观表示如下。

在这里插入图片描述

此时可以看出原先的item2的pxNext已经和pxIndex彻底断了联系。整个链表可以表示成下面样子。

在这里插入图片描述

那么很明显,第四行代码就是要把item3给pxIndex的pxPrevious了。

在这里插入图片描述

vListInsert函数

函数功能是将节点按照升序排列插入链表。

先从xListEnd开始找,其实也相当于从第一个根节点找,依次找pxNext所指向节点的xItemValue值,如果这个值大于当前待插入节点的xItemValue值,就把这个节点插到当前的这个节点后面(当前这个节点的pxNext所指向节点的前面)。

核心代码如下。

	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;

举个例子,item2插入到item1后面。进行到第三步的时候,直观表示如下。

在这里插入图片描述

此时可以发现,item3的pxPrevious已经和item1断了联系。所以上图可表示如下。

在这里插入图片描述

那么最后一步显而易见,把item1的pxNext指向item2即可。此时item2就插进去了。

在这里插入图片描述

uxListRemove函数

这个函数功能是将节点从链表中删除。核心代码如下。

pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

如果删除item2,直观表示如下:

在这里插入图片描述

#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;
}