FreeRTOS-02-列表和列表項

4 列表和列表項

列表是FreeRTOS中的一個數據結構,概念上和鏈表有點相似,列表被用來跟蹤FreeRTOS中的任務。shell

4.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;

列表項ListItem_t的定義以下:函數

typedef struct xLIST_ITEM ListItem_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         //檢查列表完整性
};

Mini列表項MiniListItem_t的定義以下:ui

typedef struct xMINI_LIST_ITEM MiniListItem_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; //指向前一個列表項
};

4.2 列表和列表項初始化

列表初始化:列表初始化是對列表結構體List_t中的各個成員變量進行初始化,列表的初始化經過vListInitialise()來完成,函數實現以下:code

void vListInitialise( List_t * const pxList )
{
    pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd );  //(1)
    pxList->xListEnd.xItemValue = portMAX_DELAY;  //(2)
    pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd );  //(3) 
    pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );//(4) 
    pxList->uxNumberOfItems = ( UBaseType_t ) 0U;//(5)
    
    listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList );//(6)
    listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList );//(7)
}

(1)xListEnd用來表示列表的末尾,pxIndex表示列表項的索引號,此時只有一個列表項xListEnd。blog

(2)xListEnd的列表項值初始化爲portMAX_DELAY,這是個宏,不一樣MCU值不一樣,可能爲0xfff或0xffffffffULL。索引

(3)xListEnd的pxNext變量指向自身。內存

(4)xListEnd的pxPrevious變量指向自身。原型

(5)此時沒有其餘列表項,uxNumberOfItems爲0,這裏沒有算xListEnd。it

(6)(7)初始化列表項中用於完整性檢查字段。

初始化以後的結果以下:

image-20210725101513251

列表項初始化:列表項在使用的時候也須要初始化。列表項初始化函數爲vListInitialiseItem()。

void vListInitialiseItem( ListItem_t * const pxItem )
{
    pxItem->pxContainer = NULL;

    listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
    listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
}

列表項初始化僅將列表項成員pxContainer初始化爲NULL。

4.3 列表和列表項相關函數

4.3.1 列表項插入函數

函數原型:

#include "FreeRTOS.h"
#include "list.h"

void vListInsert( List_t * const pxList,
                  ListItem_t * const pxNewListItem );

函數說明:列表項插入函數。列表項的插入位置由列表項中成員變量xItemValue決定,根據xItemValue的值反照升序的方式排列。

參數說明:pxList:列表項要插入的列表;pxNewListItem:要插入的列表項。

函數實現:

#include "FreeRTOS.h"
#include "list.h"

void vListInsert( List_t * const pxList,
                  ListItem_t * const pxNewListItem )
{
    ListItem_t * pxIterator;
    const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; //(1)

    listTEST_LIST_INTEGRITY( pxList );  //(2)
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    if( xValueOfInsertion == portMAX_DELAY ) //(3)
    {
        pxIterator = pxList->xListEnd.pxPrevious; //(4)
    }
    else
    {
        for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); 
            pxIterator->pxNext->xItemValue <= xValueOfInsertion; 
            pxIterator = pxIterator->pxNext ) //(5) 
        {
            /* There is nothing to do here, 
             * just iterating to the wanted insertion position. */
        }
    }

    pxNewListItem->pxNext = pxIterator->pxNext;  //(6)
    pxNewListItem->pxNext->pxPrevious = pxNewListItem;
    pxNewListItem->pxPrevious = pxIterator;
    pxIterator->pxNext = pxNewListItem;

    pxNewListItem->pxContainer = pxList; //(7)

    ( pxList->uxNumberOfItems )++; //(8)
}

(1)獲取要插入的列表項值,根據這個值確認列表項的插入位置。

(2)檢查列表和列表項 的完整性。

(3)插入的列表項值爲portMAX_DELAY,插入的位置就在列表最末尾。

(4)插入的列表項值爲portMAX_DELAY,列表項插入在xListEnd前面。

(5)插入的列表項值不等於portMAX_DELAY,在列表中一個一個找插入的位置,找到合適的位置就退出,當前for循環體沒有代碼,這個查找過程就按照升序的方式查找列表項插入點。

(6)從本行開始的四行代碼就是將列表項插入到列表中,插入過程和雙向鏈表相似。

(7)列表項的成員變量pxContainer記錄此列表項屬於哪一個列表。

(8)列表的成員變量uxNumberOfItems加一,表示又添加了一個列表項。

列表項插入過程演示:

在一個空的列表中插入三個列表項,這三個列表項的值分別爲40、60、50。

一、插入值爲40的列表項

在一個空的列表插入一個列表值爲40的列表項ListIterm1,插入完成後以下圖所示,可看出列表是一個環形列表:

image-20210725115230037

二、插入值爲60的列表項

在上面列表中插入值爲60的列表項,插入完成後以下圖所示:

image-20210725115832549

三、插入值爲50的列表項

在上面列表中插入值爲50的列表項,插入完成後以下圖所示:

image-20210725120234596

4.3.2 列表項末尾插入函數

函數原型:

#include "FreeRTOS.h"
#include "list.h"

void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem );

函數說明:列表末尾插入列表項。

參數說明:pxList:列表項要插入的列表;pxNewListItem:要插入的列表項。

函數實現:

void vListInsertEnd( List_t * const pxList,
                     ListItem_t * const pxNewListItem )
{
    ListItem_t * const pxIndex = pxList->pxIndex;

    listTEST_LIST_INTEGRITY( pxList ); //(1)
    listTEST_LIST_ITEM_INTEGRITY( pxNewListItem );

    pxNewListItem->pxNext = pxIndex;  //(2)
    pxNewListItem->pxPrevious = pxIndex->pxPrevious;

    mtCOVERAGE_TEST_DELAY();

    pxIndex->pxPrevious->pxNext = pxNewListItem;
    pxIndex->pxPrevious = pxNewListItem;

    pxNewListItem->pxContainer = pxList; //(3)

    ( pxList->uxNumberOfItems )++;  //(4)
}

(1)這行和下一行代碼完成對列表和列表項的完整性檢查。

(2)從本行開始的代碼就是將要插入的列表項插入到列表末尾。這裏所謂的末尾是根據列表的成員變量pxIndex來肯定的。pxIndex成員變量用於遍歷列表,pxIndex所指向的列表項就是要遍歷的開始列表項,也就是pxIndex所指向的列表項就表明列表頭。新的列表項就插入到pxIndex所指向的列表項的前面。

(3)標記新的列表項pxNewListItem屬於列表pxList。

(4)記錄列表中的列表項數目的變量加一,更新列表項數目。

列表項插入過程演示:

一、默認列表

插入列表項前先準備一個默認列表,以下圖所示:

image-20210725133720641

二、插入值爲50的列表項

在上面的列表中插入一個值爲50的列表項,插入完成以後以下圖所示:

image-20210725134005947

4.3.2 列表項刪除函數

函數原型:

#include "FreeRTOS.h"
#include "list.h"

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove );

函數說明:列表項刪除函數。

參數說明:pxItemToRemove:要刪除的列表項。注意:列表項的刪除只是將指定的列表項從列表中刪除掉,並不會將這個列表項的內存給釋放掉,若是這個列表項是動態內存分配的話。

返回值:返回刪除列表項之後的列表剩餘列表項數目。

函數實現:

UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove )
{
    List_t * const pxList = pxItemToRemove->pxContainer;  //(1)

    pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; //(2)
    pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext;

    mtCOVERAGE_TEST_DELAY();

    if( pxList->pxIndex == pxItemToRemove )
    {
        pxList->pxIndex = pxItemToRemove->pxPrevious; //(3)
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    pxItemToRemove->pxContainer = NULL;  //(4)
    ( pxList->uxNumberOfItems )--;

    return pxList->uxNumberOfItems;  //(5)
}

(1)讀取列表項中的成員pxContainer,獲得列表項處於哪一個列表中。

(2)與下一行完成列表項的刪除。將要刪除的列表項的先後兩個列表項鍊接在一塊兒。

(3)若是列表的pxIndex正好指向要刪除的列表項,則pxIndex從新指向被刪除的列表項的前一個列表項。

(4)被刪除列表項的成員變量pxContainer清零。

(5)返回新列表的當前列表項數目。

4.3.2 列表的遍歷

列表的遍歷是經過listGET_OWNER_OF_NEXT_ENTRY宏實現的。每調用一次這個函數列表的pxIndex變量就會指向下一個列表項,而且返回這個列表項的pxOwner變量值。

宏的實現:

#include "FreeRTOS.h"
#include "list.h"

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                              \//(1)
    {                                                                             \
        List_t * const pxConstList = ( pxList );                                  \
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;              \//(2)
        if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd))\//(3)
        {                                                                                   \
            ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                    \//(4)
        }                                                                                   \
        ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                      \//(5)
    }

(1)pxTCB用來保存pxIndex所指向的列表項的pvOwner變量值,也就是這個列表項的全部者,一般是一個任務的任務控制塊。pxList表示要遍歷的列表。

(2)列表的pxIndex變量指向下一個列表項。

(3)若是pxIndex指向了列表的xListEnd成員變量,表示到了列表末尾。

(4)若是到了列表末尾的話就跳過xListEnd,pxIndex再一次從新指向處於列表頭的列表項,這樣就完成了一次對列表的遍歷。

(5)將pxIndex所指向的新列表項的pvOwner賦值給pxTCB。

4.3 列表和列表項實驗

建立一個任務,用於操做列表和列表項。調用列表和列表項相關的API函數。

configSTACK_DEPTH_TYPE Task06_STACK_SIZE = 5;
UBaseType_t  Task06_Priority = 1;
TaskHandle_t Task06_xHandle;

List_t TestList;
ListItem_t ListItem1;
ListItem_t ListItem2;
ListItem_t ListItem3;


void vTask06_Code(void *para)
{
    //初始化列表和列表項
    vListInitialise(&TestList);
    vListInitialiseItem(&ListItem1);
    vListInitialiseItem(&ListItem2);
    vListInitialiseItem(&ListItem3);
    ListItem1.xItemValue = 40;
    ListItem2.xItemValue = 60;
    ListItem3.xItemValue = 50;

    //打印列表和其餘列表項地址
    PRINT(" ==== list and listItem address ====");
    PRINT(" type                address");
    PRINT(" ------------------------------");
    PRINT(" TestList             %#x", &TestList);
    PRINT(" TestList->pxIndex    %#x", TestList.pxIndex);
    PRINT(" TestList->xListEnd   %#x", &TestList.xListEnd);
    PRINT(" ListItem1            %#x", &ListItem1);
    PRINT(" ListItem2            %#x", &ListItem2);
    PRINT(" ListItem3            %#x", &ListItem3);
    PRINT(" =============== end ===============\n");

    //向列表中添加列表項ListItem1,觀察列表項中成員變量的值。
    vListInsert(&TestList, &ListItem1);
    PRINT(" ====== add listItem1 in TestList =======");
    PRINT(" type                           address");
    PRINT(" ----------------------------------------");
    PRINT(" TestList->xListEnd->pxNext      %#x", TestList.xListEnd.pxNext);
    PRINT(" ListItem1->pxNext               %#x", ListItem1.pxNext);
    PRINT(" -------- 先後向鏈接切割線 ---------");
    PRINT(" TestList->xListEnd->pxPrevious  %#x", TestList.xListEnd.pxPrevious);
    PRINT(" ListItem1->pxPrevious           %#x", ListItem1.pxPrevious);
    PRINT(" ================= end ==================\n");

    //向列表中插入列表項ListItem2,觀察列表項中成員變量的變化
    vListInsert(&TestList, &ListItem2);
    PRINT(" ====== add listItem2 in TestList =======");
    PRINT(" type                           address");
    PRINT(" ----------------------------------------");
    PRINT(" TestList->xListEnd->pxNext      %#x", TestList.xListEnd.pxNext);
    PRINT(" ListItem1->pxNext               %#x", ListItem1.pxNext);
    PRINT(" ListItem2->pxNext               %#x", ListItem2.pxNext);
    PRINT(" -------- 先後向鏈接切割線 ---------");
    PRINT(" TestList->xListEnd->pxPrevious  %#x", TestList.xListEnd.pxPrevious);
    PRINT(" ListItem1->pxPrevious           %#x", ListItem1.pxPrevious);
    PRINT(" ListItem2->pxPrevious           %#x", ListItem2.pxPrevious);
    PRINT(" ================= end ==================\n");

    //向列表中插入列表項ListItem3,觀察列表項中成員變量的變化
    vListInsert(&TestList, &ListItem3);
    PRINT(" ====== add listItem3 in TestList =======");
    PRINT(" type                           address");
    PRINT(" ----------------------------------------");
    PRINT(" TestList->xListEnd->pxNext      %#x", TestList.xListEnd.pxNext);
    PRINT(" ListItem1->pxNext               %#x", ListItem1.pxNext);
    PRINT(" ListItem3->pxNext               %#x", ListItem3.pxNext);
    PRINT(" ListItem2->pxNext               %#x", ListItem2.pxNext);
    PRINT(" -------- 先後向鏈接切割線 ---------");
    PRINT(" TestList->xListEnd->pxPrevious  %#x", TestList.xListEnd.pxPrevious);
    PRINT(" ListItem1->pxPrevious           %#x", ListItem1.pxPrevious);
    PRINT(" ListItem3->pxPrevious           %#x", ListItem3.pxPrevious);
    PRINT(" ListItem2->pxPrevious           %#x", ListItem2.pxPrevious);
    PRINT(" ================= end ==================\n");

    //刪除ListItem2,觀察列表項中成員變量的變化
    uxListRemove(&ListItem2);
    PRINT(" ========== delete listItem2 ============");
    PRINT(" type                           address");
    PRINT(" ----------------------------------------");
    PRINT(" TestList->xListEnd->pxNext      %#x", TestList.xListEnd.pxNext);
    PRINT(" ListItem1->pxNext               %#x", ListItem1.pxNext);
    PRINT(" ListItem3->pxNext               %#x", ListItem3.pxNext);
    PRINT(" -------- 先後向鏈接切割線 ---------");
    PRINT(" TestList->xListEnd->pxPrevious  %#x", TestList.xListEnd.pxPrevious);
    PRINT(" ListItem1->pxPrevious           %#x", ListItem1.pxPrevious);
    PRINT(" ListItem3->pxPrevious           %#x", ListItem3.pxPrevious);
    PRINT(" ================= end ==================\n");

    //pxIndex向後移一項
    TestList.pxIndex = TestList.pxIndex->pxNext;
    //在列表末尾插入列表項ListItem2
    vListInsertEnd(&TestList, &ListItem2);
    PRINT(" ===== add listItem2 in TestList end =====");
    PRINT(" type                           address");
    PRINT(" ----------------------------------------");
    PRINT(" TestList->pxIndex               %#x", TestList.pxIndex);
    PRINT(" TestList->xListEnd->pxNext      %#x", TestList.xListEnd.pxNext);
    PRINT(" ListItem2->pxNext               %#x", ListItem2.pxNext);
    PRINT(" ListItem1->pxNext               %#x", ListItem1.pxNext);
    PRINT(" ListItem3->pxNext               %#x", ListItem3.pxNext);
    PRINT(" -------- 先後向鏈接切割線 ---------");
    PRINT(" TestList->xListEnd->pxPrevious  %#x", TestList.xListEnd.pxPrevious);
    PRINT(" ListItem2->pxPrevious           %#x", ListItem2.pxPrevious);
    PRINT(" ListItem1->pxPrevious           %#x", ListItem1.pxPrevious);
    PRINT(" ListItem3->pxPrevious           %#x", ListItem3.pxPrevious);
    PRINT(" ================= end ==================\n");
    
    for (;;)
    {
        static unsigned int cnt = 0;
        PRINT(" task06 cnt %u...", cnt);
        cnt++;
        vTaskDelay(1000);
    }
}

void creat_task_test_list(void)
{
    if (xTaskCreate(vTask06_Code, "list test task06", Task06_STACK_SIZE, 
        NULL, Task06_Priority, &Task06_xHandle) != pdPASS)
        PRINT("creat task failed!\n");
}

編譯,運行,打印結果以下:

$ ./build/freertos-simulator 
 ==== list and listItem address ====
 type                address
 ------------------------------
 TestList             0x60f000
 TestList->pxIndex    0x60f010
 TestList->xListEnd   0x60f010
 ListItem1            0x60f0c0
 ListItem2            0x60f120
 ListItem3            0x60f080
 =============== end ===============

 ====== add listItem1 in TestList =======
 type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0
 ListItem1->pxNext               0x60f010
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f0c0
 ListItem1->pxPrevious           0x60f010
 ================= end ==================

 ====== add listItem2 in TestList =======
 type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0
 ListItem1->pxNext               0x60f120
 ListItem2->pxNext               0x60f010
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f120
 ListItem1->pxPrevious           0x60f010
 ListItem2->pxPrevious           0x60f0c0
 ================= end ==================

 ====== add listItem3 in TestList =======
 type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0
 ListItem1->pxNext               0x60f080
 ListItem3->pxNext               0x60f120
 ListItem2->pxNext               0x60f010
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f120
 ListItem1->pxPrevious           0x60f010
 ListItem3->pxPrevious           0x60f0c0
 ListItem2->pxPrevious           0x60f080
 ================= end ==================

 ========== delete listItem2 ============
 type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0
 ListItem1->pxNext               0x60f080
 ListItem3->pxNext               0x60f010
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f080
 ListItem1->pxPrevious           0x60f010
 ListItem3->pxPrevious           0x60f0c0
 ================= end ==================

 ===== add listItem2 in TestList end =====
 type                           address
 ----------------------------------------
 TestList->pxIndex               0x60f0c0
 TestList->xListEnd->pxNext      0x60f120
 ListItem2->pxNext               0x60f0c0
 ListItem1->pxNext               0x60f080
 ListItem3->pxNext               0x60f010
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f080
 ListItem2->pxPrevious           0x60f010
 ListItem1->pxPrevious           0x60f120
 ListItem3->pxPrevious           0x60f0c0
 ================= end ==================

 task06 cnt 0...
 task06 cnt 1...

過程分析:

(1)列表和列表項的地址以下:

TestList             0x60f000
 ListItem1            0x60f0c0
 TestList->pxIndex    0x60f010
 TestList->xListEnd   0x60f010
 ListItem2            0x60f120
 ListItem3            0x60f080

(2)列表TestList添加列表項ListItem1後的地址以下:

type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0  //ListItem1
 ListItem1->pxNext               0x60f010  //TestList->xListEnd
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f0c0  //ListItem1
 ListItem1->pxPrevious           0x60f010  //TestList->xListEnd

用示意圖表示以下圖所示:

image-20210725194739330

(2)列表TestList添加列表項ListItem2後的地址以下:

type                           address
 ----------------------------------------
 TestList->xListEnd->pxNext      0x60f0c0  //ListItem1
 ListItem1->pxNext               0x60f120  //ListItem2
 ListItem2->pxNext               0x60f010  //TestList->xListEnd
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f120  //ListItem2
 ListItem1->pxPrevious           0x60f010  //TestList->xListEnd
 ListItem2->pxPrevious           0x60f0c0  //ListItem1

用示意圖表示以下圖所示:

image-20210725195658588

(3)列表TestList插入列表項ListItem3後的地址以下:

TestList->xListEnd->pxNext      0x60f0c0  //ListItem1
 ListItem1->pxNext               0x60f080  //ListItem3
 ListItem3->pxNext               0x60f120  //ListItem2
 ListItem2->pxNext               0x60f010  //TestList->xListEnd
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f120  //ListItem2
 ListItem1->pxPrevious           0x60f010  //TestList->xListEnd
 ListItem3->pxPrevious           0x60f0c0  //ListItem1
 ListItem2->pxPrevious           0x60f080  //ListItem3

用示意圖表示以下圖所示:

image-20210725200451252

(4)列表TestList刪除列表項ListItem2以後的地址以下:

TestList->xListEnd->pxNext      0x60f0c0  //ListItem1
 ListItem1->pxNext               0x60f080  //ListItem3
 ListItem3->pxNext               0x60f010  //TestList->xListEnd
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f080  //ListItem3
 ListItem1->pxPrevious           0x60f010  //TestList->xListEnd
 ListItem3->pxPrevious           0x60f0c0  //ListItem1

用示意圖表示以下圖所示:

image-20210725201711692

(4)列表TestList在末端插入列表項ListItem2以後的地址以下:

這裏所說的末端就是TestList->pxIndex指向的列表項(表明列表頭),新的列表項就插入到TestList->pxIndex所指向的列表項的前面。

TestList->pxIndex               0x60f0c0  //ListItem1
 TestList->xListEnd->pxNext      0x60f120  //ListItem2
 ListItem2->pxNext               0x60f0c0  //ListItem1
 ListItem1->pxNext               0x60f080  //ListItem3
 ListItem3->pxNext               0x60f010  //TestList->xListEnd
 -------- 先後向鏈接切割線 ---------
 TestList->xListEnd->pxPrevious  0x60f080  //ListItem3
 ListItem2->pxPrevious           0x60f010  //TestList->xListEnd
 ListItem1->pxPrevious           0x60f120  //ListItem2
 ListItem3->pxPrevious           0x60f0c0  //ListItem1

用示意圖表示以下圖所示:

image-20210725201601814

相關文章
相關標籤/搜索