文章目录
- [07. FreeRTOS列表与列表项](#07. FreeRTOS列表与列表项)
-
- [1. 列表和列表项的简介](#1. 列表和列表项的简介)
- [2. 列表相关API函数](#2. 列表相关API函数)
- [3. 代码验证](#3. 代码验证)
07. FreeRTOS列表与列表项
1. 列表和列表项的简介
列表的定义:
c
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;
列表项的定义:
c
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; /* 重定义成 ListItem_t */
迷你列表项:
c
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; /* 重定义成 MiniListItem_t */
列表和列表项的关系:
初始状态:
2. 列表相关API函数
函数 | 描述 |
---|---|
vListInitialise() | 初始化列表 |
vListInitialiseItem() | 初始化列表项 |
vListInsertEnd() | 列表末尾插入列表项 |
vListInsert() | 列表插入列表项 |
uxListRemove() | 列表移除列表项 |
-
函数vListInitialise()
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化,只有初始化后的列表,才能够正常地被使用。列表初始化的过程,其实就是初始化列表中的成员变量。
-
函数vListInitialiseItem()
此函数用于初始化列表项,在定义列表项之后,也需要先对其进行初始化,只有初始化完的列表项,才能够被正常地使用。列表项初始化的过程,也是初始化列表项中的成员变量。
-
函数vListInsert()
此函数用于将待插入列表的列表项按照列表项值升序排序的顺序,有序地插入到列表中。
插入示意图:
初始状态列表:
插入40后的列表:
插入60后的列表:
插入50后的列表:
代码具体体现:
cvoid vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) { ListItem_t * pxIterator; //* 获取列表项的数值依据数值升序排列 */ const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; /* Only effective when configASSERT() is also defined, these tests may catch * the list data structures being overwritten in memory. They will not catch * data errors caused by incorrect configuration or use of FreeRTOS. */ //* 检查参数是否正确 */ listTEST_LIST_INTEGRITY( pxList ); listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); /* Insert the new list item into the list, sorted in xItemValue order. * * If the list already contains a list item with the same item value then the * new list item should be placed after it. This ensures that TCBs which are * stored in ready lists (all of which have the same xItemValue value) get a * share of the CPU. However, if the xItemValue is the same as the back marker * the iteration loop below will not end. Therefore the value is checked * first, and the algorithm slightly modified if necessary. */ //* 如果待插入列表项的值为最大值 */ if( xValueOfInsertion == portMAX_DELAY ) { //* 插入的位置为列表 xListEnd 前面 */ pxIterator = pxList->xListEnd.pxPrevious; } else { /* *** NOTE *********************************************************** * If you find your application is crashing here then likely causes are * listed below. In addition see https://www.FreeRTOS.org/FAQHelp.html for * more tips, and ensure configASSERT() is defined! * https://www.FreeRTOS.org/a00110.html#configASSERT * * 1) Stack overflow - * see https://www.FreeRTOS.org/Stacks-and-stack-overflow-checking.html * 2) Incorrect interrupt priority assignment, especially on Cortex-M * parts where numerically high priority values denote low actual * interrupt priorities, which can seem counter intuitive. See * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html and the definition * of configMAX_SYSCALL_INTERRUPT_PRIORITY on * https://www.FreeRTOS.org/a00110.html * 3) Calling an API function from within a critical section or when * the scheduler is suspended, or calling an API function that does * not end in "FromISR" from an interrupt. * 4) Using a queue or semaphore before it has been initialised or * before the scheduler has been started (are interrupts firing * before vTaskStartScheduler() has been called?). * 5) If the FreeRTOS port supports interrupt nesting then ensure that * the priority of the tick interrupt is at or below * configMAX_SYSCALL_INTERRUPT_PRIORITY. **********************************************************************/ //*遍历列表中的列表项,找到插入的位置 for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ { /* There is nothing to do here, just iterating to the wanted * insertion position. */ } } //* 将待插入的列表项插入指定位置 */ pxNewListItem->pxNext = pxIterator->pxNext; pxNewListItem->pxNext->pxPrevious = pxNewListItem; pxNewListItem->pxPrevious = pxIterator; pxIterator->pxNext = pxNewListItem; /* Remember which list the item is in. This allows fast removal of the * item later. */ //* 更新待插入列表项所在列表 */ pxNewListItem->pxContainer = pxList; //* 更新列表中列表项的数量 */ ( pxList->uxNumberOfItems )++; }
-
函数vListInsertEnd()
此函数用于将待插入列表的列表项插入到列表 pxIndex 指针指向列表项的前面,是一种无序的插入方法。
插入示意图:
插入前:
插入30后的列表项:
插入前:
插入30后的列表项:
-
函数uxListRemove()
此函数用于将列表项从列表项所在列表中移除.
移除列表项2示意图:
3. 代码验证
本实验主要实现FreeRTOS的列表项的插入与删除,定义三个任务函数,开始任务用于创建其他任务;任务一用于LED灯闪烁,提示系统正常工作;任务二用于进行列表项的插入与删除。
-
函数入口:
用于创建开始任务并开启任务调度
c/*函数入口*/ void freertos_Dynamic_Create(void) { lcd_show_string(10, 10, 220, 32, 32, "STM32", RED); lcd_show_string(10, 47, 220, 24, 24, "Task Create&Delete", LIGHTGREEN); lcd_show_string(15, 80, 110, 16, 16, "Task1: 000", GREEN); lcd_show_string(135, 80, 110, 16, 16, "Task2: 000", GREEN); xTaskCreate((TaskFunction_t ) start_task, //指向任务函数的指针 (char * ) "start_task", //任务名称 (configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,//任务堆栈大小,字节为单位 (void * ) NULL, //传递给任务函数的参数 (UBaseType_t ) START_TASK_PRIO, //任务优先级 (TaskHandle_t * ) &start_task_handler //任务句柄:任务控制块 ); vTaskStartScheduler(); //开启任务调度 }
-
开始任务:
用于创建任务一和任务二
cvoid start_task(void* pvParamter) { taskENTER_CRITICAL(); // 进入临界区 xTaskCreate((TaskFunction_t ) task1, //指向任务函数的指针 (char * ) "task1", //任务名称 (configSTACK_DEPTH_TYPE) TASK1_TASK_STACK_SIZE, //任务堆栈大小,字节为单位 (void * ) NULL, //传递给任务函数的参数 (UBaseType_t ) TASK1_TASK_PRIO, //任务优先级 (TaskHandle_t * ) &task1_task_handler //任务句柄:任务控制块 ); xTaskCreate((TaskFunction_t ) task2, //指向任务函数的指针 (char * ) "task2", //任务名称 (configSTACK_DEPTH_TYPE) TASK2_TASK_STACK_SIZE, //任务堆栈大小,字节为单位 (void * ) NULL, //传递给任务函数的参数 (UBaseType_t ) TASK2_TASK_PRIO, //任务优先级 (TaskHandle_t * ) &task2_task_handler //任务句柄:任务控制块 ); vTaskDelete(NULL); taskEXIT_CRITICAL(); // 退出临界区 }
-
任务一:
实现LED0每500ms翻转一次
cvoid task1(void* pvParamter) { uint32_t task1_num = 0; while(1) { lcd_show_xnum(71, 80, ++task1_num, 3, 16, 0x80, GREEN); LED0_TOGGLE(); vTaskDelay(500); } }
-
任务二:
进行列表和列表项的操作
-
初始化列表
cvListInitialise(&TestList); //初始化列表 vListInitialiseItem(&ListItem1); //初始化列表项1 vListInitialiseItem(&ListItem2); //初始化列表项2 vListInitialiseItem(&ListItem3); //初始化列表项3 ListItem1.xItemValue = 40; ListItem2.xItemValue = 60; ListItem3.xItemValue = 50;
- 列表初始化
cvListInitialise(&TestList);
- 功能 :初始化一个列表
TestList
。 - 作用 :
TestList
是一个 FreeRTOS 列表的头部结构体,列表在初始化后将会处于空状态,准备好用于插入或管理列表项。
- 列表项初始化
cvListInitialiseItem(&ListItem1); vListInitialiseItem(&ListItem2); vListInitialiseItem(&ListItem3);
- 功能 :初始化三个列表项
ListItem1
、ListItem2
和ListItem3
。 - 作用:每个列表项结构体在初始化后将会被设置为一个空的列表项,即这些列表项还未插入到任何列表中,且它们的前后指针将指向自身。
- 设置列表项值
cListItem1.xItemValue = 40; ListItem2.xItemValue = 60; ListItem3.xItemValue = 50;
- 功能 :为每个列表项设置一个值,
xItemValue
。 - 作用 :
xItemValue
是 FreeRTOS 列表项中的一个成员,用于存储与列表项相关的值。这个值可以用来对列表项进行排序或者优先级排序。通常,在 FreeRTOS 中,列表项的值越小,优先级越高。
-
打印列表和其他列表项的地址
c/* 第二步:打印列表和其他列表项的地址 */ printf("/**************第二步:打印列表和列表项的地址**************/\r\n"); printf("项目\t\t\t地址\r\n"); printf("TestList\t\t0x%p\t\r\n", &TestList); printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex); printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd)); printf("ListItem1\t\t0x%p\t\r\n", &ListItem1); printf("ListItem2\t\t0x%p\t\r\n", &ListItem2); printf("ListItem3\t\t0x%p\t\r\n", &ListItem3); printf("/**************************结束***************************/\r\n\r\n");
-
打印列表和列表项地址:
cprintf("TestList\t\t0x%p\t\r\n", &TestList);
- 功能 :打印
TestList
列表头部结构体的地址。 - 解释 :
&TestList
是TestList
的内存地址,这个地址指向整个列表结构体。
- 功能 :打印
-
打印
TestList
的pxIndex
成员的地址:cprintf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
- 功能 :打印
TestList
中pxIndex
成员的地址。 - 解释 :
pxIndex
是TestList
列表结构体中的一个成员,通常指向当前列表项的索引。这里打印的是该成员的值,实际上是TestList
中pxIndex
成员所指向的地址。
- 功能 :打印
-
打印
TestList
的xListEnd
成员的地址:cprintf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
- 功能 :打印
TestList
中xListEnd
成员的地址。 - 解释 :
xListEnd
是TestList
列表结构体中的一个成员,表示列表的结束位置。这里打印的是该成员的地址。
- 功能 :打印
-
打印列表项地址:
cprintf("ListItem1\t\t0x%p\t\r\n", &ListItem1); printf("ListItem2\t\t0x%p\t\r\n", &ListItem2); printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
- 功能:打印三个列表项的内存地址。
- 解释 :
&ListItem1
、&ListItem2
和&ListItem3
分别是这三个列表项的内存地址,用于调试和验证这些结构体的存储位置。
-
实验结果:
-
-
列表项1插入列表
c/* 第三步:列表项1插入列表 */ printf("/*****************第三步:列表项1插入列表******************/\r\n"); vListInsert((List_t* )&TestList, /* 列表 */ (ListItem_t*)&ListItem1); /* 列表项 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("/**************************结束***************************/\r\n\r\n");
插入操作
cvListInsert((List_t*)&TestList, (ListItem_t*)&ListItem1);
- 功能 :将
ListItem1
插入到TestList
列表中。 - 解释 :
vListInsert
是 FreeRTOS 提供的函数,用于将一个列表项插入到指定的列表中。此操作会将ListItem1
插入到TestList
中的正确位置,通常是按照xItemValue
的顺序。
打印列表项的指针状态
打印输出语句的目的是验证插入操作后的列表状态。
cprintf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
- 功能 :打印
TestList
列表的xListEnd
成员的pxNext
指针的地址。 - 解释 :
pxNext
指向列表末尾的下一个列表项。在插入操作后,xListEnd
的pxNext
应该指向插入的列表项(ListItem1
)。
cprintf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
- 功能 :打印
ListItem1
的pxNext
指针的地址。 - 解释 :
pxNext
是ListItem1
的next
指针。在插入操作后,ListItem1
的pxNext
应该指向xListEnd
(即列表的末尾)。
cprintf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
- 功能 :打印
TestList
列表的xListEnd
成员的pxPrevious
指针的地址。 - 解释 :
pxPrevious
指向列表末尾的前一个列表项。在插入操作后,xListEnd
的pxPrevious
应该指向ListItem1
。
cprintf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
- 功能 :打印
ListItem1
的pxPrevious
指针的地址。 - 解释 :
pxPrevious
是ListItem1
的previous
指针。在插入操作后,ListItem1
的pxPrevious
应该指向xListEnd
(即列表的末尾)。
综合分析
通过这段代码,你可以验证
ListItem1
是否成功插入到TestList
列表中。插入操作完成后,应该能看到以下情况:TestList->xListEnd->pxNext
应该指向ListItem1
。ListItem1->pxNext
应该指向xListEnd
。TestList->xListEnd->pxPrevious
应该指向ListItem1
。ListItem1->pxPrevious
应该指向xListEnd
。
实验结果
- 功能 :将
-
列表项2插入列表
cprintf("/*****************第四步:列表项2插入列表******************/\r\n"); vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem2); /* 插入 ListItem2 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious)); printf("/**************************结束***************************/\r\n\r\n");
这段代码的目的是将
ListItem2
插入到TestList
列表中,并打印出插入操作后的列表状态,以便检查和验证列表结构是否正确更新。具体来说,这里主要关注插入后列表项的连接情况。下面是详细的分析:代码功能
c/* 第四步:列表项2插入列表 */ printf("/*****************第四步:列表项2插入列表******************/\r\n"); vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem2); /* 插入 ListItem2 到 TestList 列表 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious)); printf("/**************************结束***************************/\r\n\r\n");
插入操作
cvListInsert((List_t*)&TestList, (ListItem_t*)&ListItem2);
- 功能 :将
ListItem2
插入到TestList
列表中。 - 解释 :此操作将
ListItem2
插入到TestList
列表中的正确位置,通常是按照xItemValue
的顺序。在插入之前,ListItem1
已经在列表中,ListItem2
会根据它的xItemValue
值决定它的插入位置。
打印列表项的指针状态
打印输出语句的目的是检查
ListItem2
插入后列表项之间的连接状态:cprintf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
- 功能 :打印
TestList
的xListEnd
成员的pxNext
指针的地址。 - 解释 :插入
ListItem2
后,xListEnd
的pxNext
应该指向ListItem2
,因为ListItem2
将成为列表的新的末尾项。
cprintf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
- 功能 :打印
ListItem1
的pxNext
指针的地址。 - 解释 :插入
ListItem2
后,ListItem1
的pxNext
应该指向ListItem2
,因为ListItem2
将跟在ListItem1
之后。
cprintf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
- 功能 :打印
ListItem2
的pxNext
指针的地址。 - 解释 :插入
ListItem2
后,ListItem2
的pxNext
应该指向xListEnd
,即列表的末尾。
cprintf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
- 功能 :打印
TestList
的xListEnd
成员的pxPrevious
指针的地址。 - 解释 :插入
ListItem2
后,xListEnd
的pxPrevious
应该指向ListItem2
,因为ListItem2
现在是列表的最后一个有效项。
cprintf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
- 功能 :打印
ListItem1
的pxPrevious
指针的地址。 - 解释 :插入
ListItem2
后,ListItem1
的pxPrevious
应该指向xListEnd
,因为ListItem1
之前的项是ListItem2
。
cprintf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
- 功能 :打印
ListItem2
的pxPrevious
指针的地址。 - 解释 :插入
ListItem2
后,ListItem2
的pxPrevious
应该指向ListItem1
,因为ListItem1
是ListItem2
的前一个项。
综合分析
在插入
ListItem2
后,TestList->xListEnd->pxNext
应该指向ListItem1
。ListItem1->pxNext
应该指向ListItem2
。ListItem2->pxNext
应该指向xListEnd
。TestList->xListEnd->pxPrevious
应该指向ListItem2
。ListItem1->pxPrevious
应该指向xListEnd
。ListItem2->pxPrevious
应该指向ListItem1
。
实验结果
- 功能 :将
-
列表项3插入列表
在插入
ListItem3
到TestList
列表中的过程中,我们需要分析其如何影响列表的结构。以下是详细的分析,包括插入ListItem3
后预期的指针状态。列表状态分析
cprintf("/*****************第五步:列表项3插入列表******************/\r\n"); vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem3); /* 插入 ListItem3 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext)); printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious)); printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious)); printf("/**************************结束***************************/\r\n\r\n");
结果分析:
-
TestList->xListEnd->pxNext
- 功能 :打印
TestList
的xListEnd
成员的pxNext
指针的地址。 - 预期结果 :指向
ListItem1
。在插入ListItem3
后,xListEnd
的pxNext
应指向列表的第一个有效项ListItem1
。
- 功能 :打印
-
ListItem1->pxNext
- 功能 :打印
ListItem1
的pxNext
指针的地址。 - 预期结果 :指向
ListItem3
。ListItem1
的pxNext
应指向ListItem3
,因为ListItem3
被插入在ListItem1
和ListItem2
之间。
- 功能 :打印
-
ListItem2->pxNext
- 功能 :打印
ListItem2
的pxNext
指针的地址。 - 预期结果 :指向
TestList->xListEnd
。ListItem2
是列表的最后一个有效项,因此它的pxNext
应指向xListEnd
。
- 功能 :打印
-
ListItem3->pxNext
- 功能 :打印
ListItem3
的pxNext
指针的地址。 - 预期结果 :指向
ListItem2
。ListItem3
被插入在ListItem1
和ListItem2
之间,因此它的pxNext
应指向ListItem2
。
- 功能 :打印
-
TestList->xListEnd->pxPrevious
- 功能 :打印
TestList
的xListEnd
成员的pxPrevious
指针的地址。 - 预期结果 :指向
ListItem2
。xListEnd
的pxPrevious
应指向列表的最后一个有效项,即ListItem2
。
- 功能 :打印
-
ListItem1->pxPrevious
- 功能 :打印
ListItem1
的pxPrevious
指针的地址。 - 预期结果 :指向
xListEnd
。在插入ListItem3
后,ListItem1
的pxPrevious
应指向xListEnd
,因为ListItem1
是第一个有效项。
- 功能 :打印
-
ListItem2->pxPrevious
- 功能 :打印
ListItem2
的pxPrevious
指针的地址。 - 预期结果 :指向
ListItem3
。ListItem2
的pxPrevious
应指向ListItem3
,因为ListItem3
是ListItem2
的前一个项。
- 功能 :打印
-
ListItem3->pxPrevious
- 功能 :打印
ListItem3
的pxPrevious
指针的地址。 - 预期结果 :指向
ListItem1
。ListItem3
被插入在ListItem1
和ListItem2
之间,因此它的pxPrevious
应指向ListItem1
。
- 功能 :打印
总结
在插入
ListItem3
后,TestList->xListEnd->pxNext
应指向ListItem1
。ListItem1->pxNext
应指向ListItem3
。ListItem3->pxNext
应指向ListItem2
。ListItem2->pxNext
应指向xListEnd
。TestList->xListEnd->pxPrevious
应指向ListItem2
。ListItem1->pxPrevious
应指向xListEnd
。ListItem3->pxPrevious
应指向ListItem1
。ListItem2->pxPrevious
应指向ListItem3
.
实验结果
-
-
移除列表项2
cprintf("/*******************第六步:移除列表项2********************/\r\n"); uxListRemove((ListItem_t* )&ListItem2); /* 移除列表项 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious)); printf("/**************************结束***************************/\r\n\r\n");
在调用
uxListRemove
后,ListItem2
将被从列表中断开,列表的其他项应保持连接。移除
ListItem2
后的列表状态在移除
ListItem2
后,ListItem3
和ListItem1
之间的连接将直接建立,ListItem2
将不再存在于列表中。具体指针更新如下:-
TestList->xListEnd->pxNext
- 功能 :打印
TestList
的xListEnd
成员的pxNext
指针的地址。 - 预期结果 :指向
ListItem1
。xListEnd
的pxNext
应该仍指向列表的第一个有效项ListItem1
。
- 功能 :打印
-
ListItem1->pxNext
- 功能 :打印
ListItem1
的pxNext
指针的地址。 - 预期结果 :指向
ListItem3
。
- 功能 :打印
-
ListItem3->pxNext
- 功能 :打印
ListItem3
的pxNext
指针的地址。 - 预期结果 :指向
TestList->xListEnd
。在ListItem2
被移除后,ListItem3
的pxNext
应指向xListEnd
。
- 功能 :打印
-
TestList->xListEnd->pxPrevious
- 功能 :打印
TestList
的xListEnd
成员的pxPrevious
指针的地址。 - 预期结果 :指向
ListItem3
。xListEnd
的pxPrevious
应指向列表的最后一个有效项,即ListItem3
,因为ListItem2
已被移除。
- 功能 :打印
-
ListItem1->pxPrevious
- 功能 :打印
ListItem1
的pxPrevious
指针的地址。 - 预期结果 :指向
TestList->xListEnd
。ListItem1
的pxPrevious
应指向xListEnd
,因为ListItem1
是列表中的第一个有效项。
- 功能 :打印
-
ListItem3->pxPrevious
- 功能 :打印
ListItem3
的pxPrevious
指针的地址。 - 预期结果 :指向
ListItem1
。ListItem3
的pxPrevious
应指向ListItem1
,因为ListItem3
是ListItem1
的下一个有效项。
- 功能 :打印
总结
在移除
ListItem2
后,TestList->xListEnd->pxNext
应指向ListItem1
。ListItem1->pxNext
应指向ListItem3
。ListItem3->pxNext
应指向xListEnd
。TestList->xListEnd->pxPrevious
应指向ListItem3
。ListItem1->pxPrevious
应指向xListEnd
。ListItem3->pxPrevious
应指向ListItem1
。
实验结果
-
-
-
列表末尾添加列表项2
打印输出分析
cprintf("/****************第七步:列表末尾添加列表项2****************/\r\n"); vListInsertEnd((List_t*)&TestList, (ListItem_t*)&ListItem2); /* 添加 ListItem2 到末尾 */ printf("项目\t\t\t\t地址\r\n"); printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex); printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext)); printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext)); printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext)); printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext)); printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious)); printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious)); printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious)); printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious)); printf("/************************实验结束***************************/\r\n");
解释每个打印语句的含义:
-
TestList->pxIndex
- 功能 :打印
TestList
的pxIndex
成员的地址。 - 预期结果 :应指向
ListItem1
。pxIndex
通常指向列表中的第一个有效项。
- 功能 :打印
-
TestList->xListEnd->pxNext
- 功能 :打印
TestList
的xListEnd
成员的pxNext
指针的地址。 - 预期结果 :应指向
ListItem1
。xListEnd
的pxNext
应指向列表的第一个有效项。
- 功能 :打印
-
ListItem1->pxNext
- 功能 :打印
ListItem1
的pxNext
指针的地址。 - 预期结果 :应指向
ListItem3
。ListItem1
的pxNext
应指向ListItem3
。
- 功能 :打印
-
ListItem2->pxNext
- 功能 :打印
ListItem2
的pxNext
指针的地址。 - 预期结果 :应指向
TestList->xListEnd
。因为ListItem2
被重新添加到列表的末尾,它的pxNext
应指向xListEnd
。
- 功能 :打印
-
ListItem3->pxNext
- 功能 :打印
ListItem3
的pxNext
指针的地址。 - 预期结果 :应指向
ListItem2
。ListItem3
的pxNext
应指向ListItem2
,因为ListItem2
被添加到ListItem3
的后面。
- 功能 :打印
-
TestList->xListEnd->pxPrevious
- 功能 :打印
TestList
的xListEnd
成员的pxPrevious
指针的地址。 - 预期结果 :应指向
ListItem2
。xListEnd
的pxPrevious
应指向列表的最后一个有效项,即ListItem2
。
- 功能 :打印
-
ListItem1->pxPrevious
- 功能 :打印
ListItem1
的pxPrevious
指针的地址。 - 预期结果 :应指向
TestList->xListEnd
。因为ListItem1
是列表中的第一个有效项,它的pxPrevious
应指向xListEnd
。
- 功能 :打印
-
ListItem2->pxPrevious
- 功能 :打印
ListItem2
的pxPrevious
指针的地址。 - 预期结果 :应指向
ListItem3
。因为ListItem2
被添加到列表的末尾,它的pxPrevious
应指向ListItem3
。
- 功能 :打印
-
ListItem3->pxPrevious
- 功能 :打印
ListItem3
的pxPrevious
指针的地址。 - 预期结果 :应指向
ListItem1
。因为ListItem3
现在是ListItem2
的前一个项,它的pxPrevious
应指向ListItem1
。
总结
在将
ListItem2
添加到列表末尾后,TestList->pxIndex
应指向ListItem1
。TestList->xListEnd->pxNext
应指向ListItem1
。ListItem1->pxNext
应指向ListItem3
。ListItem2->pxNext
应指向xListEnd
。ListItem3->pxNext
应指向ListItem2
。TestList->xListEnd->pxPrevious
应指向ListItem2
。ListItem1->pxPrevious
应指向xListEnd
。ListItem2->pxPrevious
应指向ListItem3
。ListItem3->pxPrevious
应指向ListItem1
。
- 功能 :打印
实验结果:
-