07.FreeRTOS列表与列表项
文章目录
- 07. FreeRTOS列表与列表项
- 1. 列表和列表项的简介
- 2. 列表相关API函数
- 3. 代码验证
07. FreeRTOS列表与列表项
1. 列表和列表项的简介
列表的定义:
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 */
列表和列表项的关系:
初始状态:
2. 列表相关API函数
函数 | 描述 |
---|---|
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示意图:
3. 代码验证
本实验主要实现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); //初始化列表项3ListItem1.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
。
- 功能:打印
实验结果:
-
相关文章:

07.FreeRTOS列表与列表项
文章目录 07. FreeRTOS列表与列表项1. 列表和列表项的简介2. 列表相关API函数3. 代码验证 07. FreeRTOS列表与列表项 1. 列表和列表项的简介 列表的定义: typedef struct xLIST {listFIRST_LIST_INTEGRITY_CHECK_VALUE /* 校验值 */volatile UBaseType_t uxN…...

餐饮业油烟净化器安装势在必行,切勿侥幸
我最近分析了餐饮市场的油烟净化器等产品报告,解决了餐饮业厨房油腻的难题,更加方便了在餐饮业和商业场所有需求的小伙伴们。 随着环保法规的日益严格和公众环保意识的提升,餐饮业油烟排放问题成为社会关注的焦点。油烟不仅影响环境质量&am…...

SpringBoot集成阿里百炼大模型 原子的学习日记Day01
文章目录 概要下一章SpringBoot集成阿里百炼大模型(多轮对话) 原子的学习日记Day02 整体架构流程技术名词解释集成步骤1,选择大模型以及获取自己的api-key(前面还有一步开通服务就没有展示啦!)2,…...

【网络编程】网络原理(一)
系列文章目录 1、 初识网络 2、网络编程的基础使用(一) 文章目录 系列文章目录前言一、端口号的使用二、UDP报文学习1.报文格式2.MD5算法 总结 前言 在前文中,主要对UDP和TCP协议有了简单的了解,而这两种协议是负责传输层的内容…...

鲁班上门维修安装系统源码开发之功能模式
鲁班上门维修安装系统在当今的趋势呈现出显著的增长与创新。随着物联网、智能家居的普及,以及消费者对便捷、高效生活方式的追求,鲁班上门维修安装系统凭借其多渠道预约、智能派单、在线支付与费用明细透明等优势,赢得了市场的广泛认可。 …...

图数据处理的新时代:阿里FraphCompute与蚂蚁金服TuGraph对比综述
目录 前言 阿里FraphCompute与蚂蚁金服TuGraph的主要特性和功能的比较: 阿里FraphCompute与蚂蚁金服TuGraph在不同应用场景分析对比: 阿里FraphCompute与蚂蚁金服TuGraph未来趋势的对比: FraphCompute与TuGraph详解 缺点劣势深入比较 前言…...

InnoDB引擎下SQL的执行流程
SQL执行流程 连接器 客户端连接驱动与mysql连接池连接 半双工通信传入客户端的sql 查询缓存(8.0之后没有) 删除原因 如果每次查询条件不同导致命中率低没有命中缓存 创建新缓存在创建缓存的时候会添加表级锁缓存更新需要批量失效 sql解析器 对传入的sql 词法分析 分解成各种t…...

Java小白入门到实战应用教程-重写和重载
引言 在上一节中我们学习了面向对象中的继承,然后在那一节中我们提到了一个知识点叫做:重写。 通过上节的代码样例我们也观察到了,重写是发生在子类和父类的这种继承关系中。 继承的特点就是提取所有子类共有的属性和方法,但是…...

微力同步如何安装使用并使用内网穿透配置公网地址远程访问
文章目录 1.前言2. 微力同步网站搭建2.1 微力同步下载和安装2.2 微力同步网页测试2.3 内网穿透工具安装 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 私有云盘作为云存储概念的延伸,虽然谈不上多么新颖,但是其广…...

nginx负载聚能
一、负载均衡 早期的网站流量和业务功能都比较简单,单台服务器足以满足基本的需求, 但是随着互联网的发展,业务流量越来越大并且业务逻辑也跟着越来越复 杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服…...

Python进阶 JSON数据,pyecharts制图
目录 json数据格式的转换 什么是json json本质 注意 pyecharts快速入门 画一个最简单的折线图 使用全局配置选项优化折线图 总结 json数据格式的转换 什么是json 一种轻量级的数据交换格式,可以按json指定的格式去组织和封装数据 json本质 带有特定格式的…...

polyglot,一个有趣的 Python 库!
更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - polyglot。 Github地址:https://github.com/aboSamoor/polyglot 在处理多语言文本时,解析和翻译不同语言的文本数据是一个常见…...

4.3.语言模型
语言模型 假设长度为 T T T的文本序列中的词元依次为 x 1 , x 2 , ⋯ , x T x_1,x_2,\cdots,x_T x1,x2,⋯,xT。 于是, x T x_T xT( 1 ≤ t ≤ T 1\le t\le T 1≤t≤T) 可以被认为是文本序列在时间步 t t t处的观测或标签。 在给定这样的文本…...

(学习总结10)C++类和对象1
C类和对象1 一、类的定义1.类定义格式2.访问限定符3. 类域 二、实例化1.实例化概念2.对象大小 三、this指针四、C和C语言实现Stack对比 以下代码环境在 VS2022。 一、类的定义 1.类定义格式 class 为定义类的关键字,Stack 为类的名字, { } 中为类的主体…...

进击大数据系列(一):Hadoop 基本概念与生态介绍
进击大数据系列(一):Hadoop 基本概念与生态介绍-腾讯云开发者社区-腾讯云 Hadoop 简介-CSDN博客 hadoop-common-3.2.1.jar hadoop-mapreduce-client-core-3.2.1.jar hadoop-hdfs-3.2.1.jar hadoop-core 依赖之间关系...

评价类算法--模糊综合评价算法模型
一.概述 二.经典集合和模糊集合的基本概念 经常采用向量表示法来进行表示 三.隶属函数的三种确定方法 其中,梯形分布用得最多 四.应用:模糊综合评价 对应一个指定的评语: 选择一个方案:...

哪些系统需要按照等保2.0进行定级?
等保2.0适用的系统类型 根据等保2.0的要求,需要进行定级的系统主要包括但不限于以下几类: 基础信息网络:包括互联网、内部网络、虚拟专用网络等。云计算平台/系统:涵盖公有云、私有云、混合云等多种部署模式的云服务平台。大数据…...

自注意力和位置编码
一、自注意力 1、给定一个由词元组成的输入序列x1,…,xn, 其中任意xi∈R^d(1≤i≤n)。 该序列的自注意力输出为一个长度相同的序列 y1,…,yn,其中: 2、自注意力池化层将xi当作key,value,query来…...

“文件夹提示无法访问?高效数据恢复策略全解析“
一、现象解析:文件夹为何提示无法访问? 在日常使用电脑的过程中,不少用户可能会突然遇到文件夹提示“无法访问”的尴尬情况。这一提示不仅阻断了对重要文件的即时访问,还可能预示着数据丢失的风险。造成这一现象的原因多种多样&a…...

结构开发笔记(一):外壳IP防水等级与IP防水铝壳体初步选型
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/140928101 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...

WPF Datagrid控件,获取某一个单元格中的控件
在WPF应用程序中,比如需要获取特定 DataGrid 单元格中的 TextBlock 控件,可以通过访问 DataGridRow 和 DataGridCell 对象。以下是一个例子,展示如何获取 DataGrid 的第二行第一列中的 TextBlock 控件,并修改其属性。 1. 在XAML中…...

P10838 『FLA - I』庭中有奇树
前言 本题解较为基础,推导如何得出二分解题思路。 题目大意 给出无根带权树,双方采取最佳策略,求节点S->T的最短路。 有两种操作: 我方至多可以使用一次传送,花费k元从a传送到b(ab不能相邻…...

WebRTC简介
WebRTC简介 WebRTC(Web Real-Time Communication)是一项开源的实时通信技术,它允许网页浏览器进行实时语音、视频和数据共享通信,而无需安装额外的插件或应用程序。WebRTC的出现极大地简化了实时通信的开发和部署过程,…...

一套直播系统带商城源码 附搭建教程
本站没搭建测试过,有兴趣的自己折腾了,内含教程! 功能介绍: 礼物系统:普通礼物、豪华礼物、热门礼物、守护礼物、幸运礼物 提现方式:统一平台提现日期及方式,方便用户执行充值提现操作 连麦…...

Netty 总结与补充(十)
简单介绍一下 Netty?你为什么会用到Netty?说说你对Netty的认识?为什么选用Netty来做通信框架? Netty 是一个高性能、异步事件驱动的 NIO 框架,它提供了对 TCP、UDP 和文件传输的支持,作为一个异步 NIO 框架…...

循环实现异步变同步的问题
一、背景 在开发中遇到在循环中调用异步接口的问题,导致页面渲染完成时,没有展示接口返回后拼接的数组数据。二、问题 在代码中使用了map进行循环,导致调用接口的时候处于异步。this.form.list.map(async el>{el.fileList [];if(el.pic…...

测试GPT4o分析巴黎奥运会奖牌数据
使用GPT4o快速调用python代码,生成数据图表 测试GPT4o分析巴黎奥运会奖牌数据 测试GPT4o分析巴黎奥运会奖牌数据 1.首先我们让他给我们生成下当前奥运奖牌数 2.然后我们直接让GPT帮我们运行python代码,并生成奥运会奖牌图表 3.我们还可以让他帮我们…...

TF卡(SD NAND)参考设计和使用提示
目录 电路设计 Layout 设计说明 贴片注意事项 电路设计 1、参考电路: R1~R5 (10K-100 kΩ)是上拉电阻,当SD NAND处于高阻抗模式时,保护CMD和DAT线免受总线浮动。 即使主机使用SD NAND SD模式下的1位模式,主机也应通过上拉电…...

电源芯片负载调整率测试方法、原理以及自动化测试的优势-纳米软件
在芯片设计研发领域,负载调整率作为稳压电源芯片的关键性能指标,直接关系到芯片的稳定性和可靠性,因此其测试和优化显得尤为重要。以下是对负载调整率测试原理、方法以及使用ATECLOUD-IC芯片测试系统优势的进一步阐述: 负载调整率…...

C++威力强大的助手 --- const
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: C之旅 const是个奇妙且非比寻常的东西,博主从《Effective C》一书中认识到关于const更深层次的理解,写此博客进行巩固。 &#x…...