当前位置: 首页 > news >正文

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);}
    }
    
  • 任务二:

    进行列表和列表项的操作

    1. 初始化列表

      vListInitialise(&TestList);	     //初始化列表
      vListInitialiseItem(&ListItem1); //初始化列表项1
      vListInitialiseItem(&ListItem2); //初始化列表项2
      vListInitialiseItem(&ListItem3); //初始化列表项3ListItem1.xItemValue = 40;
      ListItem2.xItemValue = 60;
      ListItem3.xItemValue = 50;
      
      1. 列表初始化
      vListInitialise(&TestList);
      
      • 功能:初始化一个列表 TestList
      • 作用TestList 是一个 FreeRTOS 列表的头部结构体,列表在初始化后将会处于空状态,准备好用于插入或管理列表项。
      1. 列表项初始化
      vListInitialiseItem(&ListItem1);
      vListInitialiseItem(&ListItem2);
      vListInitialiseItem(&ListItem3);
      
      • 功能:初始化三个列表项 ListItem1ListItem2ListItem3
      • 作用:每个列表项结构体在初始化后将会被设置为一个空的列表项,即这些列表项还未插入到任何列表中,且它们的前后指针将指向自身。
      1. 设置列表项值
      ListItem1.xItemValue = 40;
      ListItem2.xItemValue = 60;
      ListItem3.xItemValue = 50;
      
      • 功能:为每个列表项设置一个值,xItemValue
      • 作用xItemValue 是 FreeRTOS 列表项中的一个成员,用于存储与列表项相关的值。这个值可以用来对列表项进行排序或者优先级排序。通常,在 FreeRTOS 中,列表项的值越小,优先级越高。
    2. 打印列表和其他列表项的地址

      /* 第二步:打印列表和其他列表项的地址 */
      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");
      
      1. 打印列表和列表项地址

        printf("TestList\t\t0x%p\t\r\n", &TestList);
        
        • 功能:打印 TestList 列表头部结构体的地址。
        • 解释&TestListTestList 的内存地址,这个地址指向整个列表结构体。
      2. 打印 TestListpxIndex 成员的地址

        printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
        
        • 功能:打印 TestListpxIndex 成员的地址。
        • 解释pxIndexTestList 列表结构体中的一个成员,通常指向当前列表项的索引。这里打印的是该成员的值,实际上是 TestListpxIndex 成员所指向的地址。
      3. 打印 TestListxListEnd 成员的地址

        printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
        
        • 功能:打印 TestListxListEnd 成员的地址。
        • 解释xListEndTestList 列表结构体中的一个成员,表示列表的结束位置。这里打印的是该成员的地址。
      4. 打印列表项地址

        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 分别是这三个列表项的内存地址,用于调试和验证这些结构体的存储位置。
      5. 实验结果:

        在这里插入图片描述

        在这里插入图片描述

    3. 列表项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 指向列表末尾的下一个列表项。在插入操作后,xListEndpxNext 应该指向插入的列表项(ListItem1)。
      printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
      
      • 功能:打印 ListItem1pxNext 指针的地址。
      • 解释pxNextListItem1next 指针。在插入操作后,ListItem1pxNext 应该指向 xListEnd(即列表的末尾)。
      printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
      
      • 功能:打印 TestList 列表的 xListEnd 成员的 pxPrevious 指针的地址。
      • 解释pxPrevious 指向列表末尾的前一个列表项。在插入操作后,xListEndpxPrevious 应该指向 ListItem1
      printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
      
      • 功能:打印 ListItem1pxPrevious 指针的地址。
      • 解释pxPreviousListItem1previous 指针。在插入操作后,ListItem1pxPrevious 应该指向 xListEnd(即列表的末尾)。

      综合分析

      通过这段代码,你可以验证 ListItem1 是否成功插入到 TestList 列表中。插入操作完成后,应该能看到以下情况:

      • TestList->xListEnd->pxNext 应该指向 ListItem1
      • ListItem1->pxNext 应该指向 xListEnd
      • TestList->xListEnd->pxPrevious 应该指向 ListItem1
      • ListItem1->pxPrevious 应该指向 xListEnd

      实验结果

      在这里插入图片描述
      在这里插入图片描述

    4. 列表项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));
      
      • 功能:打印 TestListxListEnd 成员的 pxNext 指针的地址。
      • 解释:插入 ListItem2 后,xListEndpxNext 应该指向 ListItem2,因为 ListItem2 将成为列表的新的末尾项。
      printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
      
      • 功能:打印 ListItem1pxNext 指针的地址。
      • 解释:插入 ListItem2 后,ListItem1pxNext 应该指向 ListItem2,因为 ListItem2 将跟在 ListItem1 之后。
      printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
      
      • 功能:打印 ListItem2pxNext 指针的地址。
      • 解释:插入 ListItem2 后,ListItem2pxNext 应该指向 xListEnd,即列表的末尾。
      printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
      
      • 功能:打印 TestListxListEnd 成员的 pxPrevious 指针的地址。
      • 解释:插入 ListItem2 后,xListEndpxPrevious 应该指向 ListItem2,因为 ListItem2 现在是列表的最后一个有效项。
      printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
      
      • 功能:打印 ListItem1pxPrevious 指针的地址。
      • 解释:插入 ListItem2 后,ListItem1pxPrevious 应该指向 xListEnd,因为 ListItem1 之前的项是 ListItem2
      printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
      
      • 功能:打印 ListItem2pxPrevious 指针的地址。
      • 解释:插入 ListItem2 后,ListItem2pxPrevious 应该指向 ListItem1,因为 ListItem1ListItem2 的前一个项。

      综合分析

      在插入 ListItem2 后,

      • TestList->xListEnd->pxNext 应该指向 ListItem1
      • ListItem1->pxNext 应该指向 ListItem2
      • ListItem2->pxNext 应该指向 xListEnd
      • TestList->xListEnd->pxPrevious 应该指向 ListItem2
      • ListItem1->pxPrevious 应该指向 xListEnd
      • ListItem2->pxPrevious 应该指向 ListItem1

      实验结果

      在这里插入图片描述
      在这里插入图片描述

    5. 列表项3插入列表

      在插入 ListItem3TestList 列表中的过程中,我们需要分析其如何影响列表的结构。以下是详细的分析,包括插入 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");
      

      结果分析:

      1. TestList->xListEnd->pxNext

        • 功能:打印 TestListxListEnd 成员的 pxNext 指针的地址。
        • 预期结果:指向 ListItem1。在插入 ListItem3 后,xListEndpxNext 应指向列表的第一个有效项 ListItem1
      2. ListItem1->pxNext

        • 功能:打印 ListItem1pxNext 指针的地址。
        • 预期结果:指向 ListItem3ListItem1pxNext 应指向 ListItem3,因为 ListItem3 被插入在 ListItem1ListItem2 之间。
      3. ListItem2->pxNext

        • 功能:打印 ListItem2pxNext 指针的地址。
        • 预期结果:指向 TestList->xListEndListItem2 是列表的最后一个有效项,因此它的 pxNext 应指向 xListEnd
      4. ListItem3->pxNext

        • 功能:打印 ListItem3pxNext 指针的地址。
        • 预期结果:指向 ListItem2ListItem3 被插入在 ListItem1ListItem2 之间,因此它的 pxNext 应指向 ListItem2
      5. TestList->xListEnd->pxPrevious

        • 功能:打印 TestListxListEnd 成员的 pxPrevious 指针的地址。
        • 预期结果:指向 ListItem2xListEndpxPrevious 应指向列表的最后一个有效项,即 ListItem2
      6. ListItem1->pxPrevious

        • 功能:打印 ListItem1pxPrevious 指针的地址。
        • 预期结果:指向 xListEnd。在插入 ListItem3 后,ListItem1pxPrevious 应指向 xListEnd,因为 ListItem1 是第一个有效项。
      7. ListItem2->pxPrevious

        • 功能:打印 ListItem2pxPrevious 指针的地址。
        • 预期结果:指向 ListItem3ListItem2pxPrevious 应指向 ListItem3,因为 ListItem3ListItem2 的前一个项。
      8. ListItem3->pxPrevious

        • 功能:打印 ListItem3pxPrevious 指针的地址。
        • 预期结果:指向 ListItem1ListItem3 被插入在 ListItem1ListItem2 之间,因此它的 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.

      实验结果

      在这里插入图片描述
      在这里插入图片描述

    6. 移除列表项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 后,ListItem3ListItem1 之间的连接将直接建立,ListItem2 将不再存在于列表中。具体指针更新如下:

      1. TestList->xListEnd->pxNext

        • 功能:打印 TestListxListEnd 成员的 pxNext 指针的地址。
        • 预期结果:指向 ListItem1xListEndpxNext 应该仍指向列表的第一个有效项 ListItem1
      2. ListItem1->pxNext

        • 功能:打印 ListItem1pxNext 指针的地址。
        • 预期结果:指向 ListItem3
      3. ListItem3->pxNext

        • 功能:打印 ListItem3pxNext 指针的地址。
        • 预期结果:指向 TestList->xListEnd。在 ListItem2 被移除后,ListItem3pxNext 应指向 xListEnd
      4. TestList->xListEnd->pxPrevious

        • 功能:打印 TestListxListEnd 成员的 pxPrevious 指针的地址。
        • 预期结果:指向 ListItem3xListEndpxPrevious 应指向列表的最后一个有效项,即 ListItem3,因为 ListItem2 已被移除。
      5. ListItem1->pxPrevious

        • 功能:打印 ListItem1pxPrevious 指针的地址。
        • 预期结果:指向 TestList->xListEndListItem1pxPrevious 应指向 xListEnd,因为 ListItem1 是列表中的第一个有效项。
      6. ListItem3->pxPrevious

        • 功能:打印 ListItem3pxPrevious 指针的地址。
        • 预期结果:指向 ListItem1ListItem3pxPrevious 应指向 ListItem1,因为ListItem3ListItem1 的下一个有效项。

      总结

      在移除 ListItem2 后,

      • TestList->xListEnd->pxNext 应指向 ListItem1
      • ListItem1->pxNext 应指向 ListItem3
      • ListItem3->pxNext 应指向 xListEnd
      • TestList->xListEnd->pxPrevious 应指向 ListItem3
      • ListItem1->pxPrevious 应指向 xListEnd
      • ListItem3->pxPrevious 应指向 ListItem1

      实验结果

在这里插入图片描述
在这里插入图片描述

  1. 列表末尾添加列表项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");
    

    解释每个打印语句的含义:

    1. TestList->pxIndex

      • 功能:打印 TestListpxIndex 成员的地址。
      • 预期结果:应指向 ListItem1pxIndex 通常指向列表中的第一个有效项。
    2. TestList->xListEnd->pxNext

      • 功能:打印 TestListxListEnd 成员的 pxNext 指针的地址。
      • 预期结果:应指向 ListItem1xListEndpxNext 应指向列表的第一个有效项。
    3. ListItem1->pxNext

      • 功能:打印 ListItem1pxNext 指针的地址。
      • 预期结果:应指向 ListItem3ListItem1pxNext 应指向 ListItem3
    4. ListItem2->pxNext

      • 功能:打印 ListItem2pxNext 指针的地址。
      • 预期结果:应指向 TestList->xListEnd。因为 ListItem2 被重新添加到列表的末尾,它的 pxNext 应指向 xListEnd
    5. ListItem3->pxNext

      • 功能:打印 ListItem3pxNext 指针的地址。
      • 预期结果:应指向 ListItem2ListItem3pxNext 应指向 ListItem2,因为 ListItem2 被添加到 ListItem3 的后面。
    6. TestList->xListEnd->pxPrevious

      • 功能:打印 TestListxListEnd 成员的 pxPrevious 指针的地址。
      • 预期结果:应指向 ListItem2xListEndpxPrevious 应指向列表的最后一个有效项,即 ListItem2
    7. ListItem1->pxPrevious

      • 功能:打印 ListItem1pxPrevious 指针的地址。
      • 预期结果:应指向 TestList->xListEnd。因为 ListItem1 是列表中的第一个有效项,它的 pxPrevious 应指向 xListEnd
    8. ListItem2->pxPrevious

      • 功能:打印 ListItem2pxPrevious 指针的地址。
      • 预期结果:应指向 ListItem3。因为 ListItem2 被添加到列表的末尾,它的 pxPrevious 应指向 ListItem3
    9. ListItem3->pxPrevious

      • 功能:打印 ListItem3pxPrevious 指针的地址。
      • 预期结果:应指向 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

    实验结果:
    在这里插入图片描述
    在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • leetcode 234.回文链表
  • 【C++ 面试 - 基础题】每日 3 题(一)
  • postgreSQL16添加审计功能
  • centos上传工具
  • LeetCode Hard|【460. LFU 缓存】
  • 【Cesium开发实战】视点信息功能的实现,双击保存当前视点为缩略图
  • CLEFT 基于高效大语言模型和快速微调的语言-图像对比学习
  • 利用 Angular 发挥环境的力量
  • 区块链ddos防护怎么做
  • node中使用http创建web服务器
  • C++初学(10)
  • 常见框架漏洞
  • exptern “C“的作用,在 C 和 CPP 中分别调用 openblas 中的 gemm 为例
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • oracle库PASSWORD_VERSIONS 对应的加密方式
  • 【5+】跨webview多页面 触发事件(二)
  • 07.Android之多媒体问题
  • AWS实战 - 利用IAM对S3做访问控制
  • ECMAScript入门(七)--Module语法
  • flask接收请求并推入栈
  • JAVA 学习IO流
  • Java 最常见的 200+ 面试题:面试必备
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • js继承的实现方法
  • leetcode386. Lexicographical Numbers
  • MaxCompute访问TableStore(OTS) 数据
  • NSTimer学习笔记
  • Python学习笔记 字符串拼接
  • SpringCloud集成分布式事务LCN (一)
  • 彻底搞懂浏览器Event-loop
  • 后端_ThinkPHP5
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 微信开源mars源码分析1—上层samples分析
  • ​ssh免密码登录设置及问题总结
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #APPINVENTOR学习记录
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (每日一问)基础知识:堆与栈的区别
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)Oracle存储过程编写经验和优化措施
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .NET 8 跨平台高性能边缘采集网关
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .net 按比例显示图片的缩略图
  • .net 调用海康SDK以及常见的坑解释
  • .NET 指南:抽象化实现的基类
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)