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

    实验结果:

相关推荐
枯无穷肉14 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名67715 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普15 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
云山工作室16 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
艺术家天选18 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件
向阳逐梦18 小时前
基于STM32F4单片机实现ROS机器人主板
stm32·单片机·机器人
小猪写代码19 小时前
STM32 水质水位检测项目 (调试模块)和(延时模块)
stm32·单片机·嵌入式硬件
御风_2119 小时前
STM32单片机使用CAN协议进行通信
stm32·单片机·嵌入式硬件
逝灮20 小时前
【蓝桥杯——物联网设计与开发】拓展模块3 - 温度传感器模块
驱动开发·stm32·单片机·嵌入式硬件·物联网·蓝桥杯·温度传感器
Wallace Zhang1 天前
STM32F407 | Embedded IDE01 - vscode搭建Embedded IDE开发环境(支持JLINK、STLINK、DAPLINK)
ide·vscode·stm32