07.FreeRTOS列表与列表项

文章目录

  • [07. FreeRTOS列表与列表项](#07. FreeRTOS列表与列表项)
    • [1. 列表和列表项的简介](#1. 列表和列表项的简介)
    • [2. 列表相关API函数](#2. 列表相关API函数)
    • [3. 代码验证](#3. 代码验证)

07. FreeRTOS列表与列表项

1. 列表和列表项的简介

列表的定义:

c 复制代码
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;

列表项的定义:

c 复制代码
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 */

迷你列表项:

c 复制代码
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后的列表:

    代码具体体现:

    c 复制代码
    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灯闪烁,提示系统正常工作;任务二用于进行列表项的插入与删除。

  • 函数入口:

    用于创建开始任务并开启任务调度

    c 复制代码
    /*函数入口*/
    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();  //开启任务调度
    }
  • 开始任务:

    用于创建任务一和任务二

    c 复制代码
    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翻转一次

    c 复制代码
    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. 初始化列表

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

      c 复制代码
      /* 第二步:打印列表和其他列表项的地址 */
      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. 打印列表和列表项地址

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

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

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

        c 复制代码
        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插入列表

      c 复制代码
      /* 第三步:列表项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");

      插入操作

      c 复制代码
      vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem1);
      • 功能 :将 ListItem1 插入到 TestList 列表中。
      • 解释vListInsert 是 FreeRTOS 提供的函数,用于将一个列表项插入到指定的列表中。此操作会将 ListItem1 插入到 TestList 中的正确位置,通常是按照 xItemValue 的顺序。

      打印列表项的指针状态

      打印输出语句的目的是验证插入操作后的列表状态。

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

      c 复制代码
      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 列表中,并打印出插入操作后的列表状态,以便检查和验证列表结构是否正确更新。具体来说,这里主要关注插入后列表项的连接情况。下面是详细的分析:

      代码功能

      c 复制代码
      /* 第四步:列表项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");

      插入操作

      c 复制代码
      vListInsert((List_t*)&TestList, (ListItem_t*)&ListItem2);
      • 功能 :将 ListItem2 插入到 TestList 列表中。
      • 解释 :此操作将 ListItem2 插入到 TestList 列表中的正确位置,通常是按照 xItemValue 的顺序。在插入之前,ListItem1 已经在列表中,ListItem2 会根据它的 xItemValue 值决定它的插入位置。

      打印列表项的指针状态

      打印输出语句的目的是检查 ListItem2 插入后列表项之间的连接状态:

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

      列表状态分析

      c 复制代码
      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

      c 复制代码
      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

    打印输出分析

    c 复制代码
    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

    实验结果:

相关推荐
李永奉1 小时前
STM32-认识STM32
stm32·单片机·嵌入式硬件
La Pulga2 小时前
【STM32】I2C通信—软件模拟
c语言·stm32·单片机·嵌入式硬件·mcu
典则13 小时前
STM32FreeRtos入门(四)——任务状态和调度
stm32·单片机·嵌入式硬件
充哥单片机设计13 小时前
【STM32项目开源】基于STM32的智能天然气火灾监控
stm32·单片机·嵌入式硬件
充哥单片机设计13 小时前
【STM32项目开源】基于STM32的智能仓库火灾检测系统
stm32·单片机·嵌入式硬件
就叫飞六吧15 小时前
普中stm32大Dap烧录流程
stm32
A9better15 小时前
嵌入式开发学习日志38——stm32之看门狗
stm32·嵌入式硬件·学习
小莞尔16 小时前
【51单片机】【protues仿真】基于51单片机智能路灯控制系统
c语言·stm32·单片机·嵌入式硬件·51单片机
辰哥单片机设计1 天前
TT直流减速电机(STM32)
stm32
A9better1 天前
嵌入式开发学习日志36——stm32之USART串口通信前述
stm32·单片机·嵌入式硬件·学习