C语言中的链表和节点
1 struct node
2 {
3 struct node *next; /* 指向链表的下一个节点 */
4 char data1; /* 单个的数据 */
5 unsigned char array[]; /* 数组 */
6 unsigned long *prt /* 指针数据 */
7 struct userstruct data2; /* 自定义结构体类型数据 */
8 /* ...... */
9 }
1 /* 节点定义 */
2 struct node
3 {
4 struct node *next; /* 指向链表的下一个节点 */
5 }
6
7 struct userstruct
8 {
9 /* 在结构体中,内嵌一个节点指针,通过这个节点将数据挂接到链表 */
10 struct node *next;
11 /* 各种各样......,要存储的数据 */
12 }
链表
是通过节点把离散的数据链接成一个表,通过对节点的插入和删除操作从而实现对数据的存取。数组
是通过开辟一段连续的内存来存储数据,这是数组和链表最大的区别。1 struct xLIST_ITEM
2 {
3 TickType_t xItemValue; /* 辅助值,用于帮助节点做顺序排列 */ (1)
4 struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */ (2)
5 struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */ (3)
6 void * pvOwner; /* 指向拥有该节点的内核对象,通常是TCB */ (4)
7 void * pvContainer; /* 指向该节点所在的链表 */ (5)
8 };
9 typedef struct xLIST_ITEM ListItem_t; /* 节点数据类型重定义 */ (6)
在FreeRTOS 中,凡是涉及到数据类型的地方,FreeRTOS 都会将标准的C 数据类型用typedef 重新取一个类型名
这些经过重定义的数据类型放在portmacro.h(portmacro.h 第一次使用需要在include 文件夹下面新建然后添加到工程freertos/source 这个组文件)这个头文件,中除了TickType_t 外,其它数据类型重定义是本章后面内容需要使用到,这里统一贴出来,后面将不再赘述
1 #ifndef PORTMACRO_H
2 #define PORTMACRO_H
3
4 #include "stdint.h"
5 #include "stddef.h"
6
7
8 /* 数据类型重定义 */
9 #define portCHAR char
10 #define portFLOAT float
11 #define portDOUBLE double
12 #define portLONG long
13 #define portSHORT short
14 #define portSTACK_TYPE uint32_t
15 #define portBASE_TYPE long
16
17 typedef portSTACK_TYPE StackType_t;
18 typedef long BaseType_t;
19 typedef unsigned long UBaseType_t;
20
21 #if( configUSE_16_BIT_TICKS == 1 ) (1)
22 typedef uint16_t TickType_t;
23 #define portMAX_DELAY ( TickType_t ) 0xffff
24 #else
25 typedef uint32_t TickType_t;
26 #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
27 #endif
28
29 #endif /* PORTMACRO_H */
TickType_t 具体表示16 位还是32 位, 由configUSE_16_BIT_TICKS 这个宏决定
1 #ifndef FREERTOS_CONFIG_H
2 #define FREERTOS_CONFIG_H
3
4 #define configUSE_16_BIT_TICKS 0
5
6 #endif /* FREERTOS_CONFIG_H */
1 void vListInitialiseItem( ListItem_t * const pxItem )
2 {
3 /* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
4 pxItem->pvContainer = NULL; (1)
5 }
1 typedef struct xLIST
2 {
3 UBaseType_t uxNumberOfItems; /* 链表节点计数器 */ (1)
4 ListItem_t * pxIndex; /* 链表节点索引指针 */ (2)
5 MiniListItem_t xListEnd; /* 链表最后一个节点 */ (3)
6 }List_t;
1 struct xMINI_LIST_ITEM
2 {
3 TickType_t xItemValue; /* 辅助值,用于帮助节点做升序排列 */
4 struct xLIST_ITEM * pxNext; /* 指向链表下一个节点 */
5 struct xLIST_ITEM * pxPrevious; /* 指向链表前一个节点 */
6 };
7 typedef struct xMINI_LIST_ITEM MiniListItem_t; /* 精简节点数据类型重定义 */
链表节点初始化函数在list.c 中实现,具体实现见代码,初始化好的根节点示意图具体见
1 void vListInitialise( List_t * const pxList )
2 {
3 /* 将链表索引指针指向最后一个节点 */(1)
4 pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
5
6 /* 将链表最后一个节点的辅助排序的值设置为最大,确保该节点就是链表的最后节点 */(2)
7 pxList->xListEnd.xItemValue = portMAX_DELAY;
8
9 /* 将最后一个节点的pxNext 和pxPrevious 指针均指向节点自身,表示链表为空 */(3)
10 pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
11 pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
12
13 /* 初始化链表节点计数器的值为0,表示链表为空 */(4)
14 pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
15 }
1 void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
2 {
3 ListItem_t * const pxIndex = pxList->pxIndex;
4
5 pxNewListItem->pxNext = pxIndex; ①
6 pxNewListItem->pxPrevious = pxIndex->pxPrevious; ②
7 pxIndex->pxPrevious->pxNext = pxNewListItem; ③
8 pxIndex->pxPrevious = pxNewListItem; ④
9
10 /* 记住该节点所在的链表 */
11 pxNewListItem->pvContainer = ( void * ) pxList; ⑤
12
13 /* 链表节点计数器++ */
14 ( pxList->uxNumberOfItems )++; ⑥
15 }
将节点按照升序排列插入到链表,如果有两个节点的值相同,则新节点在旧节点的后面插入
1 void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
2 {
3 ListItem_t *pxIterator;
4
5 /* 获取节点的排序辅助值 */
6 const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; (1)
7
8 /* 寻找节点要插入的位置 */ (2)
9 if ( xValueOfInsertion == portMAX_DELAY )
10 {
11 pxIterator = pxList->xListEnd.pxPrevious;
12 }
13 else
14 {
15 for ( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
16 pxIterator->pxNext->xItemValue <= xValueOfInsertion;
17 pxIterator = pxIterator->pxNext )
18 {
19 /* 没有事情可做,不断迭代只为了找到节点要插入的位置 */
20 }
21 }
22 /* 根据升序排列,将节点插入 */ (3)
23 pxNewListItem->pxNext = pxIterator->pxNext; ①
24 pxNewListItem->pxNext->pxPrevious = pxNewListItem; ②
25 pxNewListItem->pxPrevious = pxIterator; ③
26 pxIterator->pxNext = pxNewListItem; ④
27
28 /* 记住该节点所在的链表 */
29 pxNewListItem->pvContainer = ( void * ) pxList; ⑤
30
31 /* 链表节点计数器++ */
32 ( pxList->uxNumberOfItems )++; ⑥
33 }
1 UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
2 {
3 /* 获取节点所在的链表 */
4 List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
5 /* 将指定的节点从链表删除*/
6 pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; ①
7 pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; ②
8
9 /*调整链表的节点索引指针 */
10 if ( pxList->pxIndex == pxItemToRemove )
11 {
12 pxList->pxIndex = pxItemToRemove->pxPrevious;
13 }
14
15 /* 初始化该节点所在的链表为空,表示节点还没有插入任何链表 */
16 pxItemToRemove->pvContainer = NULL; ③
17
18 /* 链表节点计数器-- */
19 ( pxList->uxNumberOfItems )--; ④
20
21 /* 返回链表中剩余节点的个数 */
22 return pxList->uxNumberOfItems;
23 }
在list.h 中,还定义了各种各样的带参宏,方便对节点做一些简单的操作
1 /* 初始化节点的拥有者 */
2 #define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )\
3 ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
4
5 /* 获取节点拥有者 */
6 #define listGET_LIST_ITEM_OWNER( pxListItem )\
7 ( ( pxListItem )->pvOwner )
8
9 /* 初始化节点排序辅助值 */
10 #define listSET_LIST_ITEM_VALUE( pxListItem, xValue )\
11 ( ( pxListItem )->xItemValue = ( xValue ) )
12
13 /* 获取节点排序辅助值 */
14 #define listGET_LIST_ITEM_VALUE( pxListItem )\
15 ( ( pxListItem )->xItemValue )
16
17 /* 获取链表根节点的节点计数器的值 */
18 #define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList )\
19 ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
20
21 /* 获取链表的入口节点 */
22 #define listGET_HEAD_ENTRY( pxList )\
23 ( ( ( pxList )->xListEnd ).pxNext )
24
25 /* 获取节点的下一个节点 */
26 #define listGET_NEXT( pxListItem )\
27 ( ( pxListItem )->pxNext )
28
29 /* 获取链表的最后一个节点 */
30 #define listGET_END_MARKER( pxList )\
31 ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
32
33 /* 判断链表是否为空 */
34 #define listLIST_IS_EMPTY( pxList )\
35 ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) )
36
37 /* 获取链表的节点数 */
38 #define listCURRENT_LIST_LENGTH( pxList )\
39 ( ( pxList )->uxNumberOfItems )
40
41 /* 获取链表第一个节点的OWNER,即TCB */
42 #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
43 {
44 List_t * const pxConstList = ( pxList ); 45 /* 节点索引指向链表第一个节点 */ \
46 ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; 47 /* 这个操作有啥用? */ \
48 if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
49 {
50 ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
51 }
52 /* 获取节点的OWNER,即TCB */ \
53 ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; 54 }
1 /*
2 *************************************************************************
3 * 包含的头文件
4 *************************************************************************
5 */
6 #include "list.h"
7
8 /*
9 *************************************************************************
10 * 全局变量
11 *************************************************************************
12 */
13
14 /* 定义链表根节点 */
15 struct xLIST List_Test; (1)
16
17 /* 定义节点 */
18 struct xLIST_ITEM List_Item1; (2)
19 struct xLIST_ITEM List_Item2;
20 struct xLIST_ITEM List_Item3;
21
22
23
24 /*
25 ************************************************************************
26 * main 函数
27 ************************************************************************
28 */
29 /*
30 int main(void)
31 {
32
33 /* 链表根节点初始化 */
34 vListInitialise( &List_Test ); (3)
35
36 /* 节点1 初始化 */
37 vListInitialiseItem( &List_Item1 ); (4)
38 List_Item1.xItemValue = 1;
39
40 /* 节点2 初始化 */
41 vListInitialiseItem( &List_Item2 );
42 List_Item2.xItemValue = 2;
43
44 /* 节点3 初始化 */
45 vListInitialiseItem( &List_Item3 );
46 List_Item3.xItemValue = 3;
47
48 /* 将节点插入链表,按照升序排列 */ (5)
49 vListInsert( &List_Test, &List_Item2 );
50 vListInsert( &List_Test, &List_Item1 );
51 vListInsert( &List_Test, &List_Item3 );
52
53 for (;;)
54 {
55 /* 啥事不干 */
56 }
57 }
(1):定义链表根节点,有根了,节点才能在此基础上生长
(2):定义3 个普通节点