当前位置: 首页 > 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

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

相关文章:

07.FreeRTOS列表与列表项

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

餐饮业油烟净化器安装势在必行,切勿侥幸

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

SpringBoot集成阿里百炼大模型 原子的学习日记Day01

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

【网络编程】网络原理(一)

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

鲁班上门维修安装系统源码开发之功能模式

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

图数据处理的新时代:阿里FraphCompute与蚂蚁金服TuGraph对比综述

目录 前言 阿里FraphCompute与蚂蚁金服TuGraph的主要特性和功能的比较&#xff1a; 阿里FraphCompute与蚂蚁金服TuGraph在不同应用场景分析对比&#xff1a; 阿里FraphCompute与蚂蚁金服TuGraph未来趋势的对比&#xff1a; FraphCompute与TuGraph详解 缺点劣势深入比较 前言…...

InnoDB引擎下SQL的执行流程

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

Java小白入门到实战应用教程-重写和重载

引言 在上一节中我们学习了面向对象中的继承&#xff0c;然后在那一节中我们提到了一个知识点叫做&#xff1a;重写。 通过上节的代码样例我们也观察到了&#xff0c;重写是发生在子类和父类的这种继承关系中。 继承的特点就是提取所有子类共有的属性和方法&#xff0c;但是…...

微力同步如何安装使用并使用内网穿透配置公网地址远程访问

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

nginx负载聚能

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

Python进阶 JSON数据,pyecharts制图

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

polyglot,一个有趣的 Python 库!

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

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​&#xff08; 1 ≤ t ≤ T 1\le t\le T 1≤t≤T&#xff09; 可以被认为是文本序列在时间步 t t t处的观测或标签。 在给定这样的文本…...

(学习总结10)C++类和对象1

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

进击大数据系列(一):Hadoop 基本概念与生态介绍

进击大数据系列&#xff08;一&#xff09;&#xff1a;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的要求&#xff0c;需要进行定级的系统主要包括但不限于以下几类&#xff1a; 基础信息网络&#xff1a;包括互联网、内部网络、虚拟专用网络等。云计算平台/系统&#xff1a;涵盖公有云、私有云、混合云等多种部署模式的云服务平台。大数据…...

自注意力和位置编码

一、自注意力 1、给定一个由词元组成的输入序列x1,…,xn&#xff0c; 其中任意xi∈R^d&#xff08;1≤i≤n&#xff09;。 该序列的自注意力输出为一个长度相同的序列 y1,…,yn&#xff0c;其中&#xff1a; 2、自注意力池化层将xi当作key&#xff0c;value&#xff0c;query来…...

“文件夹提示无法访问?高效数据恢复策略全解析“

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

结构开发笔记(一):外壳IP防水等级与IP防水铝壳体初步选型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140928101 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...

WPF Datagrid控件,获取某一个单元格中的控件

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

P10838 『FLA - I』庭中有奇树

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

WebRTC简介

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

一套直播系统带商城源码 附搭建教程

本站没搭建测试过&#xff0c;有兴趣的自己折腾了&#xff0c;内含教程&#xff01; 功能介绍&#xff1a; 礼物系统&#xff1a;普通礼物、豪华礼物、热门礼物、守护礼物、幸运礼物 提现方式&#xff1a;统一平台提现日期及方式&#xff0c;方便用户执行充值提现操作 连麦…...

Netty 总结与补充(十)

简单介绍一下 Netty&#xff1f;你为什么会用到Netty&#xff1f;说说你对Netty的认识&#xff1f;为什么选用Netty来做通信框架&#xff1f; Netty 是一个高性能、异步事件驱动的 NIO 框架&#xff0c;它提供了对 TCP、UDP 和文件传输的支持&#xff0c;作为一个异步 NIO 框架…...

循环实现异步变同步的问题

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

测试GPT4o分析巴黎奥运会奖牌数据

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

TF卡(SD NAND)参考设计和使用提示

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

电源芯片负载调整率测试方法、原理以及自动化测试的优势-纳米软件

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

C++威力强大的助手 --- const

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