vxworks源码剖析- 数据结构篇一(双向链表)_1(转)

vxworks中使用了多种基本数据结构,例如双向链表,队列,树等等,本文将介绍这些基本数据结构在vxworks中的实现。
1. 双向链表
双向链表是最简单的数据结构,其实现也非常简单;而且,双向链表往往是实现其他数据结构的基础,因此本文最先介绍双向链表。双向链表的定义在dllLib.h文件中,函数实现在dllLib.c文件中。
在介绍双向链表之前,有必要先介绍一下vxworks中双向链表的实现样式:


List为一个指针,该指针链表结构体,包含两个域,分别指向链表头节点和尾节点:
typedef struct  
    {
    DL_NODE *head;
    DL_NODE *tail;
    } DL_LIST;
head和tail指针分别指向链表的头节点和尾节点,节点结构体包含两个域,分别为前一个节点和后一个节点,节点结构体的定义为:
typedef struct dlnode
    {
    struct dlnode *next;
    struct dlnode *previous;
    } DL_NODE;
1.1 双向链表创建
在创建链表时,链表还没有任何子节点,因此此时的头节点和尾节点指针均为NULL;但是链表结构体需要创建,因此需要为链表结构体分配内存,创建链表代码如下:
DL_LIST *dllCreate (void)
    {
    FAST DL_LIST *pList = (DL_LIST *) malloc ((unsigned) sizeof (DL_LIST));
    dllInit (pList);
    return (pList);
    }
STATUS dllInit
    (
    FAST DL_LIST *pList     /* #define FAST register */
    )
    {
    pList->head = NULL;
    pList->tail = NULL;
    return (OK);
    }
此时创建的链表结构只有一个链表头,但是还没有任何节点:


1.2 添加节点
vxworks中的双向链表的节点都是添加在链表的尾部,通过的dllAdd函数实现,而dllAdd函数通过调用dllInsert函数来实现,dllInsert函数实现具体的添加过程;链表节点的添加主要通过一些指针的迁移来完成,在添加过程中需要注意中间节点的存储,并注意指针迁移顺序,最重要的是千万要保证链表不能断链,说了这么多,不如看代码实在:
void dllAdd
    (
    DL_LIST *pList,     /* pointer to list descriptor */
    DL_NODE *pNode      /* pointer to node to be added */
    )
    {
    dllInsert (pList, pList->tail, pNode);
    }

void dllInsert
    (
    FAST DL_LIST *pList,        /* pointer to list descriptor */
    FAST DL_NODE *pPrev,        /* pointer to node after which to insert */
    FAST DL_NODE *pNode         /* pointer to node to be inserted */
    )
    {
    FAST DL_NODE *pNext;
    if (pPrev == NULL)
{    /* new node is to be first in list */
pNext = pList->head;                                 
pList->head = pNode;                                       /*1.设置链表结构体的头指针*/
}
    else
{    /* make prev node point fwd to new */
pNext = pPrev->next;
pPrev->next = pNode;                                     /*2.将节点添加到添加处节点的后面*/
}
    if (pNext == NULL)
pList->tail = pNode;                                    /*3.设置链表结构体的尾指针*/
    else
pNext->previous = pNode;                          /*4.将节点添加到添加处节点的后面节点的前面*/

    /* set pointers in new node */
    pNode->next = pNext;                                            /*5.设置节点的后向指针*/
    pNode->previous = pPrev;                                           /*6.设置节点的前向指针*/
    }
双向链表的添加,需要分三种情况:
1.2.1 链表中无节点
链表刚创建时就是类似的情况,此时链表结构体的头指针和尾指针均指向NULL;这种情况下添加节点主要是执行代码中的1、3、5、6语句,可以用下图表示:


转自Tony嵌入式论坛,地址:http://www.cevx.com/bbs/thread-16362-1-1.html



1.2.2 链表中有1个节点
当链表中有1个节点时,链表结构体的头指针和尾指针均指向该节点,但是该节点的前向和后向指针则为NULL,此时添加节点主要执行代码中的2、3、5、6语句,具体操作可以用下图演示:

1.2.3 链表中节点个数大于1个
如果链表中的节点个数大于1个的时候,就可以在这两个节点中间插入节点了,不过采用dllAdd函数是无法实现的,因为这个函数每次都是在链表的最后一个节点处添加节点的,因此执行的语句仍然是2、3、5、6,和前面一种情况是一样的;要实现往两个节点中间插入节点,只能通过dllInsert函数来实现,这种方式插入节点不需要改变链表结构体的头指针和尾指针,只需要改变节点的前向和后向指针就可以了,执行的语句为2、4、5、6,可以用下图来演示:


1.2.4 在链表头部添加节点
当链表中没有节点时,在链表头部添加节点和第一种情况是完全一样的,执行1、3、5、6语句;如果链表中有节点时,需要改变链表头指针的位置,执行的语句为1、4、5、6,具体情况和上面大同小异:


1.3 删除节点
节点的删除相对于节点的添加来说,要相对容易一些,因为节点的删除只需要改变两个指针即可;不过和添加节点相同的是,删除节点也需要考虑四种情况,具体代码如下:
void dllRemove
    (
    DL_LIST *pList,             /* pointer to list descriptor */
    DL_NODE *pNode              /* pointer to node to be deleted */
    )
    {
    if (pNode->previous == NULL)
pList->head = pNode->next;                                        /*1.改变链表的头指针*/
    else
pNode->previous->next = pNode->next;                     /*2.改变待删除节点的前向节点的后向指针*/
    if (pNode->next == NULL)
pList->tail = pNode->previous;                                   /*3.改变链表的尾指针*/
    else
pNode->next->previous = pNode->previous;              /*4.改变待删除节点的后向节点的前向指针*/
    }
1.3.1 删除节点为链表中唯一的一个节点
此时链表中的节点既是链表的第一个节点,也是最后一个节点,因此,删除该节点后,链表中没有节点,链表的头指针和尾指针均为NULL;此种情况下,执行的语句为1、3,具体演示如下:

1.3.2 链表中节点数大于1个,删除节点为链表的第一个节点
删除节点为链表的第一个节点,链表的尾指针将不会发生变化,头指针指向待删除节点的下一个节点,而待删除节点的后向节点的前向指针将为NULL,执行语句为1、4,具体演示如下:

1.3.3 链表中节点数大于1个,删除节点为链表的最后一个节点
删除节点为链表的最后一个节点,链表的头指针不会发生变化,尾指针指向待删除节点的前向节点,而待删除节点的前向节点的后向指针将为NULL,执行语句为2、3,具体演示如下:

1.3.4 链表中节点数大于2个,删除节点为链表中间的一个节点
此种情况最简单,只需要改变待删除节点的前向节点的后向指针和后向节点的前向指针,将待删除节点的前向节点和后向节点连接起来即可,执行语句为2、4,具体演示如下:



1.4 获取首节点
获取首节点通过dllGet函数实现,主要完成两个功能:删除首节点,返回首节点;删除首节点和删除节点功能基本相似,主要是前面描述的1.3.1和1.3.2描述的情况,至于返回首节点就更加简单了:
DL_NODE *dllGet
    (
    FAST DL_LIST *pList         /* pointer to list from which to get node */
    )
    {
    FAST DL_NODE *pNode = pList->head;
    if (pNode != NULL)                      /* is list empty? */
{
        pList->head = pNode->next;          /* make next node be 1st */
        if (pNode->next == NULL)            /* is there any next node? */
            pList->tail = NULL;             /*   no - list is empty */
        else
            pNode->next->previous = NULL;   /*   yes - make it 1st node */
}
    return (pNode);
    }
1.5 获取节点数目
获取节点数目通过dllCount函数实现,并结合DLL_NEXT宏实现,DLL_NEXT定义如下:
#define DLL_NEXT(pNode)   \
    (     \
    (((DL_NODE *)(pNode))->next) \
    )
dllCount函数实现如下:
int dllCount
    (
    DL_LIST *pList      /* pointer to list descriptor */
    )
    {
    FAST DL_NODE *pNode = DLL_FIRST (pList);
    FAST int count = 0;
    while (pNode != NULL)
{
count++;
pNode = DLL_NEXT (pNode);
}
    return (count);
    }
1.6 遍历节点
节点遍历和返回节点数目函数类似,不再多述。
1.7 删除链表
删除链表和创建链表相对应,主要是释放内存:
STATUS dllDelete
    (
    DL_LIST *pList     /* pointer to list head to be initialized */
    )
    {
    free ((char *) pList);   /* free list */
    return OK;
    }
函数内容十分简单,不过个人对此有些异议,总觉得应该先判断链表中是否有节点,如果有节点应该先删除节点并且释放掉节点内存后当链表中没有节点了才能删除链表,否则有些节点可能就成为野节点了,无法访问,但也无法释放了。不过vxworks这样写,应该有它自己的道理,毕竟我也只是看了一小部分代码,等全部看完了,应该能回答这个问题。
转自Tony嵌入式论坛,地址:http://www.cevx.com/bbs/thread-16362-1-1.html




你可能感兴趣的:(Vxworks)