赞
踩
列表的定义:
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;
列表项的定义:
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 */
迷你列表项:
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 */
列表和列表项的关系:
初始状态:
函数 | 描述 |
---|---|
vListInitialise() | 初始化列表 |
vListInitialiseItem() | 初始化列表项 |
vListInsertEnd() | 列表末尾插入列表项 |
vListInsert() | 列表插入列表项 |
uxListRemove() | 列表移除列表项 |
函数vListInitialise()
此函数用于初始化列表,在定义列表之后,需要先对其进行初始化,只有初始化后的列表,才能够正常地被使用。列表初始化的过程,其实就是初始化列表中的成员变量。
函数vListInitialiseItem()
此函数用于初始化列表项,在定义列表项之后,也需要先对其进行初始化,只有初始化完的列表项,才能够被正常地使用。列表项初始化的过程,也是初始化列表项中的成员变量。
函数vListInsert()
此函数用于将待插入列表的列表项按照列表项值升序排序的顺序,有序地插入到列表中。
插入示意图:
初始状态列表:
插入40后的列表:
插入60后的列表:
插入50后的列表:
代码具体体现:
void 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示意图:
本实验主要实现FreeRTOS的列表项的插入与删除,定义三个任务函数,开始任务用于创建其他任务;任务一用于LED灯闪烁,提示系统正常工作;任务二用于进行列表项的插入与删除。
函数入口:
用于创建开始任务并开启任务调度
/*函数入口*/ 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(); //开启任务调度 }
开始任务:
用于创建任务一和任务二
void 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翻转一次
void 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);
}
}
任务二:
进行列表和列表项的操作
初始化列表
vListInitialise(&TestList); //初始化列表
vListInitialiseItem(&ListItem1); //初始化列表项1
vListInitialiseItem(&ListItem2); //初始化列表项2
vListInitialiseItem(&ListItem3); //初始化列表项3
ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
vListInitialise(&TestList);
TestList
。TestList
是一个 FreeRTOS 列表的头部结构体,列表在初始化后将会处于空状态,准备好用于插入或管理列表项。vListInitialiseItem(&ListItem1);
vListInitialiseItem(&ListItem2);
vListInitialiseItem(&ListItem3);
ListItem1
、ListItem2
和 ListItem3
。ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
xItemValue
。xItemValue
是 FreeRTOS 列表项中的一个成员,用于存储与列表项相关的值。这个值可以用来对列表项进行排序或者优先级排序。通常,在 FreeRTOS 中,列表项的值越小,优先级越高。打印列表和其他列表项的地址
/* 第二步:打印列表和其他列表项的地址 */
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");
打印列表和列表项地址:
printf("TestList\t\t0x%p\t\r\n", &TestList);
TestList
列表头部结构体的地址。&TestList
是 TestList
的内存地址,这个地址指向整个列表结构体。打印 TestList
的 pxIndex
成员的地址:
printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
TestList
中 pxIndex
成员的地址。pxIndex
是 TestList
列表结构体中的一个成员,通常指向当前列表项的索引。这里打印的是该成员的值,实际上是 TestList
中 pxIndex
成员所指向的地址。打印 TestList
的 xListEnd
成员的地址:
printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
TestList
中 xListEnd
成员的地址。xListEnd
是 TestList
列表结构体中的一个成员,表示列表的结束位置。这里打印的是该成员的地址。打印列表项地址:
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);
&ListItem1
、&ListItem2
和 &ListItem3
分别是这三个列表项的内存地址,用于调试和验证这些结构体的存储位置。实验结果:
列表项1插入列表
/* 第三步:列表项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");
插入操作
vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem1);
ListItem1
插入到 TestList
列表中。vListInsert
是 FreeRTOS 提供的函数,用于将一个列表项插入到指定的列表中。此操作会将 ListItem1
插入到 TestList
中的正确位置,通常是按照 xItemValue
的顺序。打印列表项的指针状态
打印输出语句的目的是验证插入操作后的列表状态。
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
TestList
列表的 xListEnd
成员的 pxNext
指针的地址。pxNext
指向列表末尾的下一个列表项。在插入操作后,xListEnd
的 pxNext
应该指向插入的列表项(ListItem1
)。printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
ListItem1
的 pxNext
指针的地址。pxNext
是 ListItem1
的 next
指针。在插入操作后,ListItem1
的 pxNext
应该指向 xListEnd
(即列表的末尾)。printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
TestList
列表的 xListEnd
成员的 pxPrevious
指针的地址。pxPrevious
指向列表末尾的前一个列表项。在插入操作后,xListEnd
的 pxPrevious
应该指向 ListItem1
。printf("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插入列表
printf("/*****************第四步:列表项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
列表中,并打印出插入操作后的列表状态,以便检查和验证列表结构是否正确更新。具体来说,这里主要关注插入后列表项的连接情况。下面是详细的分析:
代码功能
/* 第四步:列表项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");
插入操作
vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem2);
ListItem2
插入到 TestList
列表中。ListItem2
插入到 TestList
列表中的正确位置,通常是按照 xItemValue
的顺序。在插入之前,ListItem1
已经在列表中,ListItem2
会根据它的 xItemValue
值决定它的插入位置。打印列表项的指针状态
打印输出语句的目的是检查 ListItem2
插入后列表项之间的连接状态:
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
TestList
的 xListEnd
成员的 pxNext
指针的地址。ListItem2
后,xListEnd
的 pxNext
应该指向 ListItem2
,因为 ListItem2
将成为列表的新的末尾项。printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
ListItem1
的 pxNext
指针的地址。ListItem2
后,ListItem1
的 pxNext
应该指向 ListItem2
,因为 ListItem2
将跟在 ListItem1
之后。printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
ListItem2
的 pxNext
指针的地址。ListItem2
后,ListItem2
的 pxNext
应该指向 xListEnd
,即列表的末尾。printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
TestList
的 xListEnd
成员的 pxPrevious
指针的地址。ListItem2
后,xListEnd
的 pxPrevious
应该指向 ListItem2
,因为 ListItem2
现在是列表的最后一个有效项。printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
ListItem1
的 pxPrevious
指针的地址。ListItem2
后,ListItem1
的 pxPrevious
应该指向 xListEnd
,因为 ListItem1
之前的项是 ListItem2
。printf("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
后预期的指针状态。
列表状态分析
printf("/*****************第五步:列表项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
printf("/*******************第六步:移除列表项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
打印输出分析
printf("/****************第七步:列表末尾添加列表项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
。实验结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。