1898_野火FreeRTOS教程阅读笔记_链表操作

1898_野火FreeRTOS教程阅读笔记_链表操作

全部学习汇总: g_FreeRTOS: FreeRTOS学习笔记 (gitee.com)

1898_野火FreeRTOS教程阅读笔记_链表操作_第1张图片

新的节点的插入,影响到的是链表中最后一个元素的后继以及当前被插入元素的前驱、后继以及归属属性。具体的操作效果为:新的节点更新自己的前驱和后继,而对等的关联信息则是当前pxIndex所指向的前驱和链表的尾结点。而链表的尾结点在初始化的时候,pxIndex存储的其实是指向链表尾结点Item的指针。因此,这里的这个赋值更新,其实是实现了让这个新的节点指向了链表的尾节点。

1898_野火FreeRTOS教程阅读笔记_链表操作_第2张图片

这第一次用到了xItemValue的元素,其实这个元素的数值算是一个元素在链表上的位置的权重信息。如果这个数值很大,意味着需要插入到链表的最后。从效果上来讲,直接调用插入到End的接口也是可行的。这里相当于把对应逻辑重新写了一遍,但是从函数调用的资源消耗角度来说,这种写法应该效率高。插入的位置点寻找原则是从List的开头向后寻找,插入到数值小于等于自己的元素最后面。而最后的链表归属以及链表中元素个数的处理,与插入End其实是一回事儿。

1898_野火FreeRTOS教程阅读笔记_链表操作_第3张图片

对于这样的数据结构设计,链表节点的删除实现相当容易:

  • 前驱之后继为吾之后继
  • 后继之前驱为吾之前驱
  • 解脱list归属关系
  • 统计节点数目需要减1

代码中还增加了一个pxIndex的处理,这个也是围绕现在的数据结构所作的特殊处理。主要是考虑到移除的节点是List最后一个节点的情况。

关于链表的测试部分可以有很多,这个教程中用到的不是很多。针对这个仿真工具我了解不多,因此除了Memory的查看之外我额外增加了辅助显示的测试代码。

1898_野火FreeRTOS教程阅读笔记_链表操作_第4张图片

上面是我修改之后的测试代码,增加了一个辅助查看信息的printf。在调试工具中,我觉得printf可能是使用最顺手的一个工具。这让我感觉到软件是活的,计算机是活的,它们是可以与我们进行交流的。

1898_野火FreeRTOS教程阅读笔记_链表操作_第5张图片

邯郸学步,我也获得了这个存储查看的信息。

1898_野火FreeRTOS教程阅读笔记_链表操作_第6张图片

进行信息打印,得出来的信息其实也很容易验证软件的功能是否符合我的期待。工作这么久,我做软件调试的时候可能还是过重依赖于高端的调试器。现在体验一下这种软件的仿真功能,感觉设计还真是不错!

我增加了一点链表操作的测试,主要是看了一下节点的删除功能。同时,也看一下链表的尾节点信息。具体的测试代码如下:

int main(void)
{
    struct xLIST_ITEM *p_item;
    UBaseType_t list_item_number;

    printf("start simulation...\n");

    vListInitialise(&List_Test);

    vListInitialiseItem(&List_Item1);
    vListInitialiseItem(&List_Item2);
    vListInitialiseItem(&List_Item3);

    List_Item1.xItemValue = 1;
    List_Item2.xItemValue = 2;
    List_Item3.xItemValue = 3;

    vListInsert(&List_Test, &List_Item2);
    vListInsert(&List_Test, &List_Item3);
    vListInsert(&List_Test, &List_Item1);

    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item end: 0x%p\n", p_item);

    printf("\ntry to remove List_Item2...\n");
    list_item_number = uxListRemove(&List_Item2);
    printf("item number of List_Test: %d\n", list_item_number);

    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);

    for(;;)
    {
        /* no code */
    }
    
    return 0;
}

运行仿真,对应的结果如下:

1898_野火FreeRTOS教程阅读笔记_链表操作_第7张图片

删除之后,继续往后接着就碰到了尾结点。这个跟预期的效果也是一样的。

进一步进行测试的扩展,看一下双向链表是否是一个环形逻辑结构。修改代码如下:

int main(void)
{
    struct xLIST_ITEM *p_item;
    UBaseType_t list_item_number;

    printf("start simulation...\n");

    vListInitialise(&List_Test);

    vListInitialiseItem(&List_Item1);
    vListInitialiseItem(&List_Item2);
    vListInitialiseItem(&List_Item3);

    List_Item1.xItemValue = 1;
    List_Item2.xItemValue = 2;
    List_Item3.xItemValue = 3;

    vListInsert(&List_Test, &List_Item2);
    vListInsert(&List_Test, &List_Item3);
    vListInsert(&List_Test, &List_Item1);

    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item end: 0x%p\n", p_item);

    printf("\ntry to remove List_Item2...\n");
    list_item_number = uxListRemove(&List_Item2);
    printf("item number of List_Test: %d\n", list_item_number);

    printf("address of List_Test: 0x%p\n", &List_Test);
    printf("address of List_Item1: 0x%p\n", &List_Item1);
    printf("address of List_Item2: 0x%p\n", &List_Item2);
    printf("address of List_Item3: 0x%p\n", &List_Item3);
    printf("number of items: %d\n", List_Test.uxNumberOfItems);
    p_item = List_Test.pxIndex->pxNext;
    printf("item 1: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 2: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 3: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 4: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 5: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 6: 0x%p\n", p_item);
    p_item = p_item->pxNext;
    printf("item 7: 0x%p\n", p_item);

    for(;;)
    {
        /* no code */
    }
    
    return 0;
}

仿真运行效果:

1898_野火FreeRTOS教程阅读笔记_链表操作_第8张图片

这个结果也是很符合预期的。

看完这部分,其实本身链表相关的技能或者知识没有什么变化。但是从仿真工具的使用上的确是收获不少。工具用着比较顺手,关于前面的代码分析不妨再进行一部分测试。

第一部分是关于节点插入函数的,我修改成了如下的逻辑:

/* 将节点按照升序排列插入到链表 */
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
	ListItem_t *pxIterator;
	
	/* 获取节点的排序辅助值 */
	const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;

	/* 寻找节点要插入的位置 */
	if( xValueOfInsertion == portMAX_DELAY )
	{
		pxIterator = pxList->xListEnd.pxPrevious;
        vListInsertEnd(pxList, pxNewListItem);
        return;
	}
	else
	{
		for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd );
		     pxIterator->pxNext->xItemValue <= xValueOfInsertion; 
			 pxIterator = pxIterator->pxNext )
		{
			/* 没有事情可做,不断迭代只为了找到节点要插入的位置 */			
		}
	}

	pxNewListItem->pxNext = pxIterator->pxNext;
	pxNewListItem->pxNext->pxPrevious = pxNewListItem;
	pxNewListItem->pxPrevious = pxIterator;
	pxIterator->pxNext = pxNewListItem;

	/* 记住该节点所在的链表 */
	pxNewListItem->pvContainer = ( void * ) pxList;

	/* 链表节点计数器++ */
	( pxList->uxNumberOfItems )++;
}

为了激活这一段代码运行,我把原来的测试代码中的一行代码做了修改:

如果分析没有错误,修改后的软件应该可以运行出来与之前一样的效果。运行效果如下:

1898_野火FreeRTOS教程阅读笔记_链表操作_第9张图片

从运行结果看,分析是准确的。

你可能感兴趣的:(FreeRTOS,笔记,链表,数据结构)