列表的相关API函数
说了这么多我们来看看列表的接口函数分别有哪些, 又是如何定义的吧, 如果你先前做过链表相关的算法题, 那么这些函数对你来说应该很熟悉吧.
列表初始化
vListInitialise
void vListInitialise( List_t * const pxList )
{
pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.xItemValue = portMAX_DELAY;
pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );
}
在列表刚刚创建的时候, 列表中是没有列表项的那么在初始化的阶段, 我们可以逐句来看
- pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );
列表中指向整个列表末尾的指针pxIndex指向的正是其自身的列表末尾项
- pxList->xListEnd.xItemValue = portMAX_DELAY;
在之后的升序排序中, 我们列表的末尾项一定是排在最后的, 那么其值在这里初始化为最大即portfMAX_DELAY(0xFFFFFFFF)
- pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );
- pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );
这两行代码原本作为列表末尾项的指向前一个列表项和指向后一个列表项的指针, 在初始化时全部都指向了自身的xListEnd
- pxList->uxNumberOfItems = ( UBaseType_t ) 0U;
这个很好理解: 刚初始化时列表项的数目为0
- listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );
这个宏在这里以及在下面都是起着一个判断的作用, 和我们的操作关系不大.
一个初始化之后列表大概是长这样的.
列表项的初始化
void vListInitialiseItem( ListItem_t * const pxItem )
{
pxItem->pvContainer = NULL;
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}
在这里, 列表项的初始化十分简单, 仅仅只需将pvContainer指针变量初始化为NULL即可.
升序插入
vListInsert
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t *pxIterator;
const TickType_t xValueOfInsertion = pxNewListItem->xItemValue;
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
if( xValueOfInsertion == portMAX_DELAY )
{
pxIterator = pxList->xListEnd.pxPrevious;
}
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 )++;
}
在这里, 我们可以看到, 列表项的插入是升序插入的, 那么我们来看看这个函数是如何实现的吧.
-
pxIterator = pxList->xListEnd.pxPrevious;
这里有一个if判断, 即是否这里的"值"是否为最大值, 如果是, 我们将pxIterator指向列表末尾项的前一个列表项, 这样是可以直接插入队尾的
for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) {}
然后, 我们开始遍历整个列表, 如果当前列表项的值小于等于要插入的列表项的值, 那么我们就继续遍历, 直到找到大于要插入的列表项的值的列表项为止. 跳出这个循环后, 这个pxIterator指向的列表项的值是最后一个小于"插入项"的列表项.
pxNewListItem->pxNext = pxIterator->pxNext;
找到要插入的位置后, 我们将要插入的列表项的pxNext指向当前列表项的下一个列表项
pxNewListItem->pxNext->pxPrevious = pxNewListItem;
pxNewListItem->pxPrevious = pxIterator;
pxIterator->pxNext = pxNewListItem;
接下来这一系列操作就是常规的双向链表的插入操作了
我们将当前列表项的下一个列表项的pxPrevious指向要插入的列表项, 将要插入的列表项的pxPrevious指向当前列表项, 最后将当前列表项的pxNext指向要插入的列表项
pxNewListItem->pvContainer = ( void * ) pxList;
最后, 我们将要插入的列表项的pvContainer指向当前列表
- ( pxList->uxNumberOfItems )++;
最后, 我们将列表的uxNumberOfItems加1, 表示列表中多了一个列表项
(来自正点原子)画图好麻烦啊........
尾插
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem )
{
ListItem_t * const pxIndex = pxList->pxIndex;
listTEST_LIST_INTEGRITY( pxList );
listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );
pxNewListItem->pxNext = pxIndex;
pxNewListItem->pxPrevious = pxIndex->pxPrevious;
mtCOVERAGE_TEST_DELAY();
pxIndex->pxPrevious->pxNext = pxNewListItem;
pxIndex->pxPrevious = pxNewListItem;
pxNewListItem->pvContainer = ( void * ) pxList;
( pxList->uxNumberOfItems )++; //列表数目++
}
这个函数的实现和上面那个函数差不多, 只是插入位置不同, 这里是直接插入队尾的. 来自正点原子
删除列表项
uxListRemove
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
mtCOVERAGE_TEST_DELAY();
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
return pxList->uxNumberOfItems;
}
List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer;
首先, 我们将要删除的列表项的pvContainer转换为List_t类型, 即要删除的列表项所在的列表
pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;
pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;
然后, 我们将要删除的列表项的下一个列表项的pxPrevious指向要删除的列表项的pxPrevious, 将要删除的列表项的pxPrevious的pxNext指向要删除的列表项的pxNext, 这样就完成了要删除的列表项的删除
if( pxList->pxIndex == pxItemToRemove )
{
pxList->pxIndex = pxItemToRemove->pxPrevious;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
然后, 我们判断要删除的列表项是否为列表的pxIndex, 如果是, 我们将列表的pxIndex指向要删除的列表项的pxPrevious, 否则, 我们什么都不做
pxItemToRemove->pvContainer = NULL;
( pxList->uxNumberOfItems )--;
最后, 我们将要删除的列表项的pvContainer置为NULL, 表示要删除的列表项已经不在任何列表中了, 然后将列表的uxNumberOfItems减1, 表示列表中少了一个列表项
(来自正点原子)