07.FreeRTOS列表与列表项

news2024/11/16 2:33:44

文章目录

  • 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); //初始化列表项3
      
      ListItem1.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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1981786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

吴恩达机器学习-可选的实验室-正则化成本和梯度

目标 在本实验中&#xff0c;你将: 用正则化项扩展前面的线性和逻辑代价函数。重新运行前面添加正则化项的过拟合示例。 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_overfit import overfit_example, output from lab_utils_common i…

关于vs2022项目占用空间太大的问题

之前在分享vs2022项目&#xff08;估计其它vs版本也差不多&#xff09;的时候发现项目占用空间比较大&#xff0c;即使压缩也不利于上传网盘&#xff0c;于是看了一下目录&#xff0c;发现有个隐藏的.vs目录&#xff0c;里面有个和项目同名的文件夹&#xff0c;占用着很大的空间…

⌈ 传知代码 ⌋ MSA+抑郁症模型总结(三)

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

NLP——Transfromer 详解

Transformer总体架构图 输入部分&#xff1a;源文本嵌入层及其位置编码器、目标文本嵌入层及其位置编码器 编码器部分 由N个编码器层堆叠而成 每个编码器层由两个子层连接结构组成 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接 第二个子层连接结构包…

Liunx---批量安装服务器

目录 一、环境准备 一、环境准备 1.准备一台rhel7的主机并且打开主机图形。 2.配置好可用ip 3.做kickstart自动安装脚本后面需要用到DHCP&#xff0c;关闭VMware DHCP功能 二、安装图形化kickstart自动安装脚本的工具 yum install system-config-kickstart ----安装图形化生…

Guitar Pro简谱怎么输入 ?如何把简谱设置到六线谱的下面?

一、Guitar Pro简谱怎么输入 简谱在音乐学习、演奏、创作和传播中都起着非常重要的作用&#xff0c;是音乐领域不可或缺的工具。吉他乐谱的制作可以使简谱&#xff0c;也可以使五线谱、六线谱等多种形式&#xff0c;这几种乐谱都可以使用Guitar Pro来完成。下面来看看Guitar Pr…

springboot大学生社会实践管理信息系统-计算机毕业设计源码61970

目 录 摘要 Abstract 1 绪论 1.1 研究背景与意义 1.2 国内外研究现状 1.3 论文结构与章节安排 2 系统分析 2.1 可行性分析 2.1.1技术可行性 2.1.2 经济可行性 2.1.3 社会可行性 2.2 系统流程分析 2.2.1 数据新增流程 2.2.2 数据删除流程 2.3 系统功能分析 2.3.…

谷歌账号被停用后,申诉没有反馈或者被拒绝后怎么办?附:谷歌账号申诉信要点和模板

有一些朋友在登录谷歌账号的时候&#xff0c;或者在是用谷歌账号的过程中突然被强制退出来&#xff0c;然后再次登录的时候就遇到了下面的提醒&#xff1a;您的账号已停用&#xff0c;而且原因通常是两大类&#xff1a;1&#xff09;谷歌账号与其他多个账号一起创建或使用的&am…

Rust 所有权

所有权 Rust的核心特性就是所有权所有程序在运行时都必须管理他们使用计算机内存的方式 有些语言有垃圾收集机制&#xff0c;在程序运行时&#xff0c;他们会不断地寻找不再使用的内存在其他语言中&#xff0c;程序员必须显式的分配和释放内存 Rust采用了第三种方式&#xff1…

FFmpeg内存对齐简述

目录 引文 行字节数的计算 ffmpeg中的align ffmpeg中的linesize 内容参考 引文 在ffmpeg的使用过程中有时会发现align这个参数&#xff0c;那么这个参数代表什么意思&#xff0c;不同的值会产生什么影响呢&#xff0c;详见下文。 行字节数的计算 理解内存对齐之前首先要…

无人机之导航系统篇

一、导航系统组成 包括惯性导航系统、卫星导航系统、视觉导航系统等。 二、导航原理 利用传感器感知无人机的位置、速度和姿态信息&#xff0c;结合地图数据和导航算法&#xff0c;计算出无人机当前的位置和航向&#xff0c;从而引导无人机按照预设的航线飞行。 三、导航精…

Linux文件或图片名称中文乱码解决【适用于centos、ubuntu等系统】

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

【unittest】TestSuite搭建测试用例示例二

1.1 打开串口示例 常用的模组则包含AT指令测试&#xff0c;或串口数据测试&#xff0c;则可添加串口配置&#xff0c;将指令通过串口发送出去&#xff0c;如下所示&#xff1a; import serial def open_serial_port(port, baudrate115200, timeout2): try: # 创建并配置串…

Vue 3+Vite+Eectron从入门到实战系列之一环境安装篇

Electron 都应该不会陌生了,是一个使用 JavaScript、HTML 和 CSS 构建桌面应用的框架。通过将 Chromium 和 Node.js 嵌入到其二进制文件中,Electron 允许你维护一个 JavaScript 代码库并创建可在 Windows、macOS 和 Linux 上运行的跨平台应用 - 无需原生开发经验。 实现效果…

YOLOv6训练自己的数据集

文章目录 前言一、YOLOv6简介二、环境搭建三、构建数据集四、修改配置文件①数据集文件配置②权重下载③模型文件配置 五、模型训练和测试模型训练模型测试 总结 前言 提示&#xff1a;本文是YOLOv6训练自己数据集的记录教程&#xff0c;需要大家在本地已配置好CUDA,cuDNN等环…

思源笔记结合群晖WebDav与cpolar内网穿透实现跨网络笔记云同步

文章目录 前言1. 开启群晖WebDav 服务2. 本地局域网IP同步测试3. 群晖安装Cpolar4. 配置远程同步地址5. 笔记远程同步测试6. 固定公网地址7. 配置固定远程同步地址 前言 本教程主要分享如何将思源笔记、cpolar内网穿透和群晖WebDav三者相结合&#xff0c;实现思源笔记的云同步…

如何使用代理IP进行电子邮件保护?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 前言 随着企业信息化的深入发展&#xff0c;电子邮件在私人生活和商业运营中起到越来越重要的作用&#xff0c;随之而来电子邮件…

掌握eBay刊登:十大工具助力卖家脱颖而出

在经济全球化的浪潮中&#xff0c;eBay作为全球最大的跨境电商平台之一&#xff0c;为卖家提供了一个展示商品、拓展市场的广阔舞台。然而&#xff0c;平台越大&#xff0c;意味着商家之间的竞争越激烈。如何在eBay上有效刊登商品&#xff0c;是卖家吸引用户的关键步骤。本文将…

500元蓝牙耳机排行榜有哪些?四款百元蓝牙耳机品牌排行推荐

在如今这个充满科技魅力的时代&#xff0c;蓝牙耳机已成为我们日常生活中不可或缺的一部分&#xff0c;无论是沉浸在音乐的世界中&#xff0c;还是在繁忙的通勤路上享受片刻宁静&#xff0c;一副优秀的蓝牙耳机都能为我们带来无与伦比的听觉享受&#xff0c;面对市场上琳琅满目…

合作文章(IF=5.9)|16s和非靶代谢组分析揭示亚麻籽木脂素对PAM过量诱导的肝毒性的保护作用

研究背景 扑热息痛&#xff08;PAM&#xff09;是世界上最常用的镇痛解热的药物之一。在肝酶细胞色素P450 Cyp2E1和Cyp1A2PAM酶的作用下&#xff0c;PAM转化为一种高活性的代谢物乙酰对位苯醌亚胺(NAPQI)&#xff0c;通过与谷胱甘肽(GSH)偶联可解毒为无毒的谷胱甘肽-NAPQI。然…