软件编程体会(准确地使用双向链表)

软件编程体会(准确地使用双向链表)

作者:燕青 2007年4月25日

链表在程序设计中是最为基本的数据结构,也是相对最容易出错环节。在C++中,我们能够使用标准的STL链表,以达到软件开发的快速性,重用性和强壮性。那么,我们在C语言中如何达到这样的效果呢?嗯,…,好像ANSC C库暂时还没有吧。现在不妨让我们自己来尝试做一个可重用的双向链表吧J

首先,让我们先浏览一下下面简单演示代码,然后再逐个仔细分析。

#ifndef _WYQ_LIST_H

#define _WYQ_LIST_H

 

#if defined( __cplusplus ) || defined( c_plusplus )

extern "C" {

#endif

 

typedef struct __WYQSListHead

{

        struct __WYQSListHead *pPrev;

        struct __WYQSListHead *pNext;

} WYQSListHead;

 

#define WYQList_Declare( listName )     /

        WYQSListHead listName = { &listName, &listName }

 

#define WYQList_Init( pListHead )                       /

        do {                                                                   /

                (pListHead)->pPrev = (pListHead);       /

                (pListHead)->pNext = (pListHead);       /

        } while ( 0 )

 

#define WYQList_Entry( ptr, type, member )      /

        ((type *) ((unsigned long) (ptr) - (unsigned long) (&((type *) 0)->member)))

 

#define WYQList_IsEmpty( pList )                ((pList) == (pList)->pNext)

 

#define WYQList_InsertBefore( pHead, pNew )     /

        WYQList_II_InsertBefore( pHead, pNew )

 

#define WYQList_InsertAfter( pHead, pNew )              /

        WYQList_II_InsertBefore( (pHead)->pNext, pNew )

 

#define WYQList_Append( pList, pNew )   /

        WYQList_InsertBefore( pList, pNew )

 

#define WYQList_Add( pList, pNew )              /

        WYQList_Append( pList, pNew )

 

#define WYQList_Remove( pHead )                                 /

        do {                                                                   /

                WYQSListHead *__pHead = (pHead);                /

                __pHead->pPrev->pNext = __pHead->pNext; /

                __pHead->pNext->pPrev = __pHead->pPrev; /

                __pHead->pPrev        = __pHead;                /

                __pHead->pNext        = __pHead;                /

        } while ( 0 )

 

#define WYQList_Splice( pList1, pList2 )                        /

        do {                                                                   /

                WYQSListHead *__pList1 = (pList1);                      /

                WYQSListHead *__pList2 = (pList2);                      /

                WYQSListHead *__pTail2 = __pList2->pPrev;       /

                __pTail2->pNext        = __pList1;                      /

                __pList2->pPrev        = __pList1->pPrev;       /

                __pList1->pPrev->pNext = __pList2;                      /

                __pList1->pPrev        = __pTail2;                      /

                /* Remove list2's header */                                    /

                __pList2->pPrev->pNext = __pList2->pNext;       /

                __pList2->pNext->pPrev = __pList2->pPrev;       /

                __pList2->pPrev        = __pList2;                      /

                __pList2->pNext        = __pList2;                      /

        } while ( 0 )

 

#define WYQList_ForEach( pList, pIter )                 /

        for ( pIter = (pList)->pNext; pIter != (pList); pIter = pIter->pNext )

 

#define WYQList_Clear( pList, destor )                          /

        do {                                                                   /

                WYQSListHead *__pList = (pList);                        /

                while ( !WYQList_IsEmpty( __pList ) )           /

                {                                                              /

                        WYQSListHead *__pIter = __pList->pNext; /

                        WYQList_Remove( __pIter );                             /

                        destor( __pIter );                                     /

                }                                                              /

        } while ( 0 )

 

#define WYQList_Reverse( pList )                                        /

        do {                                                                   /

                WYQSListHead *__pList = (pList);                        /

                WYQSListHead *__pIter = (pList);                        /

                do                                                             /

                {                                                              /

                        WYQSListHead *__pTemp = __pIter->pPrev; /

                        __pIter->pPrev        = __pIter->pNext; /

                        __pIter->pNext        = __pTemp;                /

                        __pIter               = __pIter->pPrev; /

                } while ( __pIter != __pList );                         /

        } while ( 0 )

 

#define WYQList_Visit( pList, visit )                           /

        do {                                                                   /

                WYQSListHead *__pIter;                                         /

                WYQList_ForEach( pList, __pIter )                       /

                {                                                              /

                        visit( __pIter );                                      /

                }                                                              /

        } while ( 0 )

 

#define WYQList_II_InsertBefore( pHead, pNew )  /

        do {                                                                   /

                WYQSListHead *__pHead = (pHead);                /

                WYQSListHead *__pNew  = (pNew);                 /

                __pNew->pPrev         = __pHead->pPrev; /

                __pNew->pNext         = __pHead;                /

                __pHead->pPrev->pNext = __pNew;                 /

                __pHead->pPrev        = __pNew;                 /

        } while ( 0 )

 

#if defined( __cplusplus ) || defined( c_plusplus )

}

#endif

 

#endif

WYQSListHead数据结构

它定义了一个双向链表头所包含的数据成员,一个前向指针pPrev和一个后向指针pNext,如下所示:

typedef struct __WYQSListHead

{

        struct __WYQSListHead *pPrev;

        struct __WYQSListHead *pNext;

} WYQSListHead;

我们可以使用WYQList_Declare宏来定义一个链表,也可以使用WYQList_Init来初始化这个链表头。比如:

static WYQList_Declare( list );

或者

WYQList_Init( &list );

它们两者的不同之处是,前者在定义时初始化链表,而后者是在变量分配后在初始化它。对于不同的软件场景,我们可以合适的选择它们。

WYQList_Entry

它是一个很有意思的宏,主要是将WYQSListHead的指针转化为对应数据结构的指针。请看下面的例子,它是在对应链表中查询flags满足要求的第一个链表点。

typedef struct __WYQSTestListHead

{

WYQSListHead head;

uint32_t     flags;

} WYQSTestListHead;

static WYQList_Declare( list );

WYQEResult Find( WYQSListHead *pList, uint32_t flags, WYQSTestListHead **ppHead )

{

    WYQSListHead *pIter;

WYQList_ForEach( pList, pIter )

{

    WYQSTestListHead *pHead = WYQList_Entry( pIter, WYQSTestListHead, head );

        if ( (pHead->flags & flags) == flags )

        {

            *ppHead = pHead;

            return WYQ_OK;

        }

}

*ppHead = NULL;

return WYQ_EUNFND;

}

WYQList_IsEmpty

它是用来确定链表是否为空。

WYQList_InsertBefore

它将新链表节点(pNew)插入到当前节点(pNode)之前。

WYQList_InsertAfter

它将新链表节点(pNew)插入到当前节点(pNode)之后。

WYQList_AppendWYQList_Add

它将新链表节点(pNew),追加到链表之后。不同的名字仅仅是为了个人的喜好不同而准备。

WYQList_Remove

将当前节点(pNode)从链表中删除。代码中的最后两个赋值语句是多余的,加上得目的仅仅是为了程序的强壮性。

WYQList_Splice

list2中的所有节点追加到list1中,除list2的链表头之外。这个宏对两个链表的合并操作很有用。

WYQList_Clear

清空链表中所有的节点,destor一般是在删除节点后,清理删除节点,比如内存的释放等操作。

WYQList_Reverse

list的次序整个颠倒。比如说123N变成NN-1N-21

WYQList_Visit

遍历这个链表,对每个节点调用visit操作,它一般而言只适合读或修改操作,在visit中不能从事对链表的添加或者是删除工作,否则操作未知。此外,visit可以是其他的宏,也可以是一个函数,这个根据编成者个人喜好而定J

注意点

演示程序的宏中,我们定义了很多临时的变量,看上去好像是多此一举,但是它确保证了程序的正确性。因为宏的pNode可能以pOtherNode->pNext->pPrev(它实际上等于pOtherNode)传入的,如果将它宏展开后,对应的添加或者删除操作可能是有错误的。如果编译器支持inline,那么它应该是一个更好的选择。

一个简单的演示例子

#include <stdio.h>

 

#include "WYQTypes.h"

#include "WYQAssert.h"

#include "WYQMem.h"

#include "WYQList.h"

#include "WYQ.h"

 

#define MAX_NUM         10

 

#define GEN_LVL()       ((int32_t) ((rand() / (double) RAND_MAX) * MAX_NUM + 0.5 ))

 

#define WYQSNode_Entry( p )             WYQList_Entry( p, WYQSNode, head )

#define WYQSNode_Destor( p )    WYQFree( WYQSNode_Entry( p ) )

#define WYQSNode_Visit( p )                                                    /

        do {                                                                   /

                WYQSNode *__pNode = WYQSNode_Entry( p );        /

                printf( "(%d : %d)/n", __pNode->no,             /

                        __pNode->level );                                      /

        } while ( 0 )

 

typedef struct __WYQSNode

{

        WYQSListHead head;

        int32_t      no;

        int32_t      level;

} WYQSNode;

 

static WYQList_Declare( head1 );

static WYQList_Declare( head2 );

 

static void help( void )

{

        printf( "0:/t/t quit/n" );

        printf( "1:/t/t visit list1/n" );

        printf( "2:/t/t visit list2/n" );

        printf( "3:/t/t reverse list1/n" );

        printf( "4:/t/t reverse list2/n" );

        printf( "5:/t/t splice list1 to list2/n" );

        printf( "6:/t/t splice list2 to list1/n" );

}

 

static WYQEResult init( void )

{

        int32_t i;

 

        for ( i = 0; i < MAX_NUM; ++ i )

        {

                WYQSNode *pNode = (WYQSNode *) WYQMalloc( sizeof( WYQSNode ) );

                WYQAssert( pNode != NULL );

                pNode->no       = i;

                pNode->level    = GEN_LVL();

                WYQList_Add( &head1, &pNode->head );

        }

 

        for ( i = 0; i < MAX_NUM; ++ i )

        {

                WYQSNode *pNode = (WYQSNode *) WYQMalloc( sizeof( WYQSNode ) );

                WYQAssert( pNode != NULL );

                pNode->no       = i;

                pNode->level    = GEN_LVL();

                WYQList_Add( &head2, &pNode->head );

        }

 

        return WYQ_OK;

}

 

static WYQEResult visit( int32_t who )

{

        if ( who == 1 )

                WYQList_Visit( &head1, WYQSNode_Visit );

        else if ( who == 2 )

                WYQList_Visit( &head2, WYQSNode_Visit );

        else

                return WYQ_EINV;

 

        return WYQ_OK;

}

 

static WYQEResult reverse( int32_t who )

{

        if ( who == 1 )

                WYQList_Reverse( &head1 );

        else if ( who == 2 )

                WYQList_Reverse( &head2 );

        else

                return WYQ_EINV;

 

        return WYQ_OK;

}

 

static WYQEResult splice( int32_t from, int32_t to )

{

        if ( from == 1 )

        {

                if ( to == 2 )

                        WYQList_Splice( &head2, &head1 );

                else

                        return WYQ_EINV;

        }

        else if ( from == 2 )

        {

                if ( to == 1 )

                        WYQList_Splice( &head1, &head2 );

                else

                        return WYQ_EINV;

 

        }

        else

                return WYQ_EINV;

 

        return WYQ_OK;

}

 

static WYQEResult term( void )

{

        WYQList_Clear( &head1, WYQSNode_Destor );

        WYQList_Clear( &head2, WYQSNode_Destor );

 

        return WYQ_OK;

}

 

int main( void )

{

        int32_t flag;

        int32_t cmd;

 

        WYQFunCheck( WYQ_Init() );

 

        WYQFunCheck( init() );

 

        help();

 

        for ( flag = 1; flag != 0; )

        {

 

                scanf( "%d", &cmd );

 

                switch ( cmd )

                {

                case 0:

                        flag = 0;

                        break;

 

                case 1:

                        WYQFunCheck( visit( 1 ) );

                        break;

 

                case 2:

                        WYQFunCheck( visit( 2 ) );

                        break;

 

                case 3:

                        WYQFunCheck( reverse( 1 ) );

                        break;

 

                case 4:

                        WYQFunCheck( reverse( 2 ) );

                        break;

 

                case 5:

                        WYQFunCheck( splice( 1, 2 ) );

                        break;

 

                case 6:

                        WYQFunCheck( splice( 2, 1 ) );

                        break;

 

                default:

                        help();

                        break;

                }

        }

 

        WYQFunCheck( term() );

 

        WYQFunCheck( WYQ_Term() );

 

        return 0;

}

小结

本文演示了一个简单通用的双向链表的实现,主要目的是给读者一些自己在链表使用过程中的心得体会,希望能为程序质量的提升,并给读者一些有意的启迪。

谢谢!

 

你可能感兴趣的:(数据结构,编程,list,struct,cmd,null)