创建hash的一个模板,以hash_list为例

百度百科:

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

查找性能

散列表的查找过程基本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到,另一些关键码在散列函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对散列表查找效率的量度,依然用平均查找长度来衡量。

查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:

1. 散列函数是否均匀;

2. 处理冲突的方法;

3. 散列表的装填因子。

散列表的装填因子定义为:α= 填入表中的元素个数 / 散列表的长度

α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,填入表中的元素较多,产生冲突的可能性就越大;α越小,填入表中的元素较少,产生冲突的可能性就越小。

实际上,散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。

hash函数获取hash_key举例:MOD为hash桶深度

字符串

(著名的ELFhash算法)

1

2

3

4

5

6

7

8

9

10

11

12

13

int ELFhash(char*key)

{

    unsigned long h=0;

    while(*key)

    {

        h = (h << 4) + *key++;

        unsigned long g = h & 0xF0000000L;

        if(g)

            h ^= g >> 24;

        h &= ~g;

    }

    return h % MOD;

}

前面介绍过linux内核hash链表的操作,因为本篇需要用到hash_list或者是DL_list创建hash,所以在这里先贴上hash_list/dl_list的定义及操作的头文件:hash_list.h

#ifndef _DL_LIST_H_
#define _DL_LIST_H_


#ifdef __cplusplus
extern "C"{
#endif


typedef struct tagLIST_NODE     
{
    struct tagLIST_NODE     *next;    // 指向下一个结点的指针
    struct tagLIST_NODE    **pprev;// 指向上一个结点的next指针的地址 
}HLIST_NODE_S;

typedef struct tagHLIST_HEAD    
{
    struct tagHLIST_HEAD  *first;    // 指向每一个hash桶的第一个结点的指针
}HLIST_HEAD_S;

// 初始化hash桶的头结点 
#define   INIT_HLIST_HEAD(ptr)   ((ptr)->first = NULL)

// 初始化hash桶的普通结点 
static  inline void  INIT_HLIST_NODE(HLIST_NODE_S *node)
{
    node->next   = NULL;
    node->pprev  = NULL;
}

/**
 * HLIST_Add_Head
 * @n: the element to add to the hash list.
 * @h: the list to add to.
 */
static inline void HLIST_AddHead(HLIST_NODE_S *node, HLIST_HEAD_S *head)    
{
    HLIST_NODE_S *first = head->first;
    node->next = first;
    
    if (NULL != first)
    {
        first->pprev = &node->next;
    }
    
    head->first = node;
    node->pprev = &head->first;
}


/* next must be != NULL */
/* node:要添加的新的节点。
 * next:在next节点之前添加node。
 * 在next节点的前面添加一个新的节点n,在使用这个函数中要特别注意,next不能为NULL。
 */
static inline void HLIST_AddBefore(HLIST_NODE_S *node, HLIST_NODE_S *next)
{
    node->pprev    = next->pprev;
    node->next     = next;
    next->pprev    = &node->next;
    *(node->pprev) = node;
}

/* next must be != NULL */
/* node:要添加的新的节点。
 * next:表示在next节点之后添加node。
 * 在next 节点的后面添加一个新的节点node,这里也要求next不能为NULL
 */
static inline void HLIST_AddAfter(HLIST_NODE_S *node, HLIST_NODE_S *next)
{
    next->next = node->next;
    node->next = next;
    next->pprev = &node->next;

    if(NULL != next->next)
    {
         next->next->pprev  = &next->next;
    }
}

/* node:要删除的节点。
 * 对于删除操作的话,要注意node是不是末尾节点,如果是末尾节点的话,next就是NULL?
 * 所以就没有指向的pprev,就更不能进行相应的修改了,否则进行修改。
 */
static inline void __hlist_del(HLIST_NODE_S *node)
{
    HLIST_NODE_S *next = node->next;
    HLIST_NODE_S **pprev = node->pprev;
    *pprev = next;
    if (NULL != next)
    {
        next->pprev = pprev;
    }
}


/* node:要删除的节点。
 * 在这个函数中首先删除了node节点,之后将n节点的两个指针指向了NULL,表示不可使用的地方
 */
static inline void HLIST_Del(HLIST_NODE_S *node)
{
    __hlist_del(node);
    node->next     = NULL;
    node->pprev    = NULL;
}

/*
 * 判断一个结点是否已经存在于hash桶中 
 * 判断h->prev是不是为空,如果pprev的指向是空的话,表示这个节点没有添加到这个链表当中来,
 * 如果是空,返回true,否则返回false
 */
static inline int HLIST_UnHashed(const HLIST_NODE_S *node)
{
    return !node->pprev;
}

// 判断一个hash桶是否为空 
/* head:struct hlist_head节点指针(hlist链表的头节点)。
 * 判断hlist链表是不是空链表,如果是,返回true,否则返回false。
 */
static inline int HLIST_IsEmpty(const HLIST_HEAD_S *head)
{
    return (NULL == head->first);
}



/***********************************************************
*       遍历hash链表,需要用到两个地址偏移的宏定义                            *
************************************************************/

//获取结构体成员相对于结构体的偏移
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)

//通过获取结构体中的某个成员,反推该结构体的指针 
#define container_of(ptr, type , member) ({ \
	const typeof(((type *)0)->member) *__mptr = (ptr) ; \
	(type *)((char *)__mptr - offsetof(type,member)) ;})

#define HLIST_ENTRY(ptr, type, member)  container_of(ptr, type, member)


/* pos:struct hlist_node类型的一个指针;
* head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
* 这个实际上就是一个for循环,从头到尾遍历链表。
*/
/* 普通遍历,遍历过程中不能删除节点,否则可能会出现后续节点的访问错误 */
#define HLIST_FOR_EACH(pos, head) \
    for (pos = (head)->first; {pos != NULL ; 1; }); \
     pos = pos->next)


/* 这个实际上就是一个for循环,从头到尾遍历链表。这个和前面的不同的是多了一个n,
 * 这么做是为了遍历过程中防止断链的发生。删除时用这个。
 * pos:struct hlist_node类型的一个指针;
 * next:struct hlist_node类型的一个指针,这里为区分,写作next;
 * head:struct hlist_head类型的一个指针,表示hlist链表的头结点。
 这是安全遍历,实际上就是预读取,提前指向下一个节点,遍历过程中可以删除节点
 */ 
#define HLIST_FOR_EACH_SAFE(pos, next, head) \
        for (pos = (head)->first; pos && ({ next = pos->next; 1; }); \
         pos = next)


/* ptype:用来存放遍历到的数据结构的地址,类型是type *;
 * pos:struct hlist_node类型的一个指针;
 * head:hlist链表的头结点;
 * member:struct hlist_node在type结构体中的变量的名称。
 * 通过hlist_entry 这个宏,我们可以访问hash链表所在结构体的其他成员,很多时候需要这样访问。
   本遍历宏并非安全定义,遍历过程中不能删除节点
 */
/**
 * HLIST_FOR_EACH_ENTRY    - iterate over list of given type
 * @ptype:    the type * to use as a loop cursor.
 * @pos:    the &HLIST_NODE_S to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 */
#define HLIST_FOR_EACH_ENTRY(ptype, pos, head, member)             \
            for (pos = (head)->first;                     \
             (pos != NULL) &&             \
                ({ ptype = HLIST_ENTRY(pos, typeof(*ptype), member); 1;}); \
             pos = pos->next)


/* ptype:用来存放遍历到的数据结构的地址,类型是type *;
* pos:struct hlist_node类型的一个指针;
* n:struct hlist_node类型的一个指针;
* head:hlist链表的头结点;
* member:struct hlist_node在type结构体中的变量的名称。
* 在循环中,我们就可以使用tops来指向type
* 类型结构体的任何一个变量了。这个宏函数也是为了防止在遍历的时候删除节点而引入的。
*/
/**
* HLIST_FOR_EACH_ENTRY_SAFE - iterate over list of given type safe against 
removal of list entry
* @tpos:    the type * to use as a loop cursor.
* @pos:    the &HLIST_NODE_S to use as a loop cursor.
* @n:        another &HLIST_NODE_S to use as temporary storage
* @head:    the head for your list.
* @member:    the name of the hlist_node within the struct.
*/
#define HLIST_FOR_EACH_ENTRY_SAFE(ptype, pos, next, head, member)          \
                for (pos = (head)->first;                     \
                 pos && ({ next = pos->next; 1; }) &&                  \
                    ({ ptype = HLIST_ENTRY(pos, typeof(*ptype), member); 1;}); \
                 pos = next)


#ifndef __cplusplus
}
#endif  // end of__cplusplus
#endif // end of _DL_LIST_H_

根据以上结构,我们创建hash表,首先为了便于区分,我们先重命名一下HLIST_HEAD_S 和HLIST_NODED_S这两个在上面头文件中定义的结构:分别为HASH_LIST_S,HASH_NODE_S。

1.定义hash结构及访问的宏定义:

hash结构是以上文的头文件定义的hash_list重命名后定义的,访问宏定义也用到以上的访问宏,所以在看hash_table结构及访问的时候,需要结合上半部分头文件看,上边的头文件就是之前的hash-list一文中介绍的,当中纠正一个括号不对称的错误。

#ifndef ULONG
#define ULONG unsigned long
#endif


#ifndef LONG 
#define LONG long
#endif


typedef HLIST_NODE_S HASH_NODE_S;/* hash table node */
typedef HLIST_HEAD_S HASH_LIST_S;/* hash config list */


typedef struct Hash_Table
{
    ULONG ulSize;
    ULONG (*pfHash)(const void *);/* hash function */
    HASH_LIST_S *pstBckt;
}HASH_TABLE_S;



#define HASH_GET_INDEX(pstTbl, pKey)    ((pstTbl)->pfHash(pKey))
#define HASH_IS_VALID_INDEX(pstTbl, ulIndex)    ((ulIndex) < (pstTbl)->ulSize)
#define HASH_GET_LIST(pstTbl, ulIndex)  (&(pstTbl)->pstBckt[ulIndex])


#define HASH_ENTRY(ptr, type, member) container_of(ptr, type, member)

#define HASH_TBL_FOREACH(pstTbl, ulIndex) \
        for((ulIndex) = 0; (ulIndex) < (pstTbl)->ulSize; (ulIndex)++)

#define HASH_BUCKT_FOREACH(pstList, pstNode)   \
                    HLIST_FOR_EACH(pstNode, pstList)

#define HASH_BUCKT_FOREACH_SAFE(pstList, pstNode, pstNext)  \
                    HLIST_FOR_EACH_SAFE(pstNode, pstNext, pstList)

#define HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)    \
                    HASH_BUCKT_FOREACH(HASH_GET_LIST(pstTbl, ulIndex), pstNode)

#define HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext) \
                    HASH_BUCKT_FOREACH_SAFE(HASH_GET_LIST(pstTbl, ulIndex), pstNode, pstNext)

#define HASH_SCAN_TBL(pstTbl, ulIndex, pstNode) \
                    HASH_TBL_FOREACH(pstTbl, ulIndex) \
                    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)

#define HASH_SCAN_TBL_SAFE(pstTbl, ulIndex, pstNode, pstNext)   \
                    HASH_TBL_FOREACH(pstTbl, ulIndex)   \
                    HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext)

2.创建hash:

这里需要传入获取hash index 的函数,作为hash索引,函数根据需要的key值计算所添加的节点所在hash桶的索引,然后添加到该索引对应的链表中:创建hash主要是申请资源并做初始化工作。

/* callback is hash function to get hash index */
HASH_TABLE_S * HASH_Create(ULONG ulSize, ULONG (*pfHash)(const void *))
{
    HASH_TABLE_S *pstTbl = NULL;
    ULONG ulLen = 0, i = 0;
    
    if (0 == ulSize || NULL == pfHash)
    {
        return (HASH_TABLE_S *)NULL;
    }

    ulLen= sizeof(HASH_TABLE_S) + sizeof(HASH_LIST_S) * ulSize;
    pstTbl = (HASH_TABLE_S *)malloc(ulLen);

    if (NULL != pstTbl)
    {
        pstTbl->pstBckt = (HASH_LIST_S *)(pstTbl +1);
        pstTbl->ulSize = ulSize;
        pstTbl->pfHash = pfHash;

        for(i = 0; i < ulSize; i++)
        {
            INIT_HLIST_HEAD(pstTbl->pstBckt[i]);
        }
    }

    return pstTbl;
}

3.添加hash节点:

其实都是用的hash_list节点的添加函数:具体函数见上文中的hash_list.h

#define HASH_Add_List(pstList, pstNode) \
        HLIST_Add_Head(pstNode, pstList)
void HASH_Add(HASH_TABLE_S *pstTbl, HASH_NODE_S *pstNode, const void *pKey)
{
    HASH_LIST_S *pstList = NULL;
    ULONG ulIndex = 0;

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    pstList = HASH_GET_LIST(pstTbl, ulIndex);
    HASH_Add_List(pstList, pstNode);
    
    return;
}

void HASH_ListAddAfter(HASH_LIST_S * pstList, HASH_NODE_S *pstPrev, 
                                                     HASH_NODE_S *pstInst)
{
    if (NULL == pstPrev)
    {
        HLIST_AddHead((HASH_LIST_NODE_S *)pstInst , (HASH_LIST_HEAD_S *) pstList);
    }
    else
    {
        HLIST_AddAfter((HASH_LIST_NODE_S *)pstInst, (HASH_LIST_NODE_S *)pstList);
    }

    return ;
}

void HASH_AddOrder(HASH_TABLE_S *pstTbl, HASH_NODE_S *pstNode,
                        LONG (*pfKeyCmp)(const HASH_NODE_S*, const void*),
                        const void *pKey)
{
    HASH_LIST_S *pstList = NULL;
    HASH_NODE_S *pstPrevNode = (HASH_NODE_S *)NULL;
    HASH_NODE_S pstLoopNode = NULL;
    ULONG ulIndex = 0;

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    pstList = HASH_GET_LIST(pstTbl, ulIndex);

    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstLoopNode)
    {
        if (pfKeyCmp(pstLoopNode, pKey))
        {
            break;
        }
        pstPrevNode = pstLoopNode;
    }
    
    HASH_ListAddAfter(pstList, pstPrevNode, pstNode);
    return;
}

4.删除hash节点:

用的依然是hash_list.h的已有函数

void HASH_Del(HASH_NODE_S *pstNode)
{
    HLIST_Del(pstNode);
}

void HASH_Free(HASH_NODE_S *pstNode)
{
    free(pstNode);
}

5.释放hash tale:

删除全部节点并释放,释放函数可以自定义:遍历hash table的宏定义用在前面

/* pfFree = HASH_FreeNode */
void HASH_Destroy(HASH_TABLE_S *pstTbl, void (*pfFree)(HASH_NODE_S*))                        
{
    HASH_NODE_S *pstNode = NULL;
    HASH_NODE_S *pstNext = NULL;
    ULONG ulIndex = 0;

    if (NULL == pstTbl)
    {
        return ;
    }

    if (NULL != pfFree)
    {
        HASH_TBL_FOREACH(pstTbl, ulIndex)
        {
            /* safe to del */
            HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext)
            {
                (*pfFree)(pstNode);
            }
        }
    }

    free((HASH_TABLE_S *)pstTbl);

    return;
}

6.最后,查找hash 

查找函数自定义:

HASH_NODE_S * HASH_Find(HASH_TABLE_S *pstTbl, const void * pKey,
                            LONG (*pfKeyCmp)(const HASH_NODE_S *, const void *))
{
    HASH_NODE_S *pstNode = NULL;
    ULONG ulIndex = 0;

    if ((NULL == pstTbl) || (NULL == pKey) || (NULL == pfKeyCmp))
    {
        return (HASH_NODE_S *)NULL;
    }

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    if (!HASH_IS_VALID_INDEX(pstTbl, ulIndex))
    {
        return (HASH_NODE_S *)NULL;
    }

    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)
    {
        if (0 == pfKeyCmp(pstNode, pKey))
        {
            break;
        }
    }
    
    return pstNode;
}

完整的hash_table.c文件如下:并没有编译过

#ifdef __cplusplus
extern "C"{
#endif

#include "dl_list.h"


#ifndef ULONG
#define ULONG unsigned long
#endif


#ifndef LONG 
#define LONG long
#endif


typedef HLIST_NODE_S HASH_NODE_S;/* hash table node */
typedef HLIST_HEAD_S HASH_LIST_S;/* hash config list */


typedef struct Hash_Table
{
    ULONG ulSize;
    ULONG (*pfHash)(const void *);/* hash function */
    HASH_LIST_S *pstBckt;
}HASH_TABLE_S;



#define HASH_GET_INDEX(pstTbl, pKey)    ((pstTbl)->pfHash(pKey))
#define HASH_IS_VALID_INDEX(pstTbl, ulIndex)    ((ulIndex) < (pstTbl)->ulSize)
#define HASH_GET_LIST(pstTbl, ulIndex)  (&(pstTbl)->pstBckt[ulIndex])


#define HASH_ENTRY(ptr, type, member) container_of(ptr, type, member)

#define HASH_TBL_FOREACH(pstTbl, ulIndex) \
        for((ulIndex) = 0; (ulIndex) < (pstTbl)->ulSize; (ulIndex)++)

#define HASH_BUCKT_FOREACH(pstList, pstNode)   \
                    HLIST_FOR_EACH(pstNode, pstList)

#define HASH_BUCKT_FOREACH_SAFE(pstList, pstNode, pstNext)  \
                    HLIST_FOR_EACH_SAFE(pstNode, pstNext, pstList)

#define HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)    \
                    HASH_BUCKT_FOREACH(HASH_GET_LIST(pstTbl, ulIndex), pstNode)

#define HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext) \
                    HASH_BUCKT_FOREACH_SAFE(HASH_GET_LIST(pstTbl, ulIndex), pstNode, pstNext)

#define HASH_SCAN_TBL(pstTbl, ulIndex, pstNode) \
                    HASH_TBL_FOREACH(pstTbl, ulIndex) \
                    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)

#define HASH_SCAN_TBL_SAFE(pstTbl, ulIndex, pstNode, pstNext)   \
                    HASH_TBL_FOREACH(pstTbl, ulIndex)   \
                    HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext)


/* callback is hash function to get hash index */
HASH_TABLE_S * HASH_Create(ULONG ulSize, ULONG (*pfHash)(const void *))
{
    HASH_TABLE_S *pstTbl = NULL;
    ULONG ulLen = 0, i = 0;
    
    if (0 == ulSize || NULL == pfHash)
    {
        return (HASH_TABLE_S *)NULL;
    }

    ulLen= sizeof(HASH_TABLE_S) + sizeof(HASH_LIST_S) * ulSize;
    pstTbl = (HASH_TABLE_S *)malloc(ulLen);

    if (NULL != pstTbl)
    {
        pstTbl->pstBckt = (HASH_LIST_S *)(pstTbl +1);
        pstTbl->ulSize = ulSize;
        pstTbl->pfHash = pfHash;

        for(i = 0; i < ulSize; i++)
        {
            INIT_HLIST_HEAD(pstTbl->pstBckt[i]);
        }
    }

    return pstTbl;
}


#define HASH_Add_List(pstList, pstNode) \
        HLIST_Add_Head(pstNode, pstList)
void HASH_Add(HASH_TABLE_S *pstTbl, HASH_NODE_S *pstNode, const void *pKey)
{
    HASH_LIST_S *pstList = NULL;
    ULONG ulIndex = 0;

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    pstList = HASH_GET_LIST(pstTbl, ulIndex);
    HASH_Add_List(pstList, pstNode);
    
    return;
}

void HASH_ListAddAfter(HASH_LIST_S * pstList, HASH_NODE_S *pstPrev, 
                                                     HASH_NODE_S *pstInst)
{
    if (NULL == pstPrev)
    {
        HLIST_AddHead((HASH_LIST_NODE_S *)pstInst , (HASH_LIST_HEAD_S *) pstList);
    }
    else
    {
        HLIST_AddAfter((HASH_LIST_NODE_S *)pstInst, (HASH_LIST_NODE_S *)pstList);
    }

    return ;
}

void HASH_AddOrder(HASH_TABLE_S *pstTbl, HASH_NODE_S *pstNode,
                        LONG (*pfKeyCmp)(const HASH_NODE_S*, const void*),
                        const void *pKey)
{
    HASH_LIST_S *pstList = NULL;
    HASH_NODE_S *pstPrevNode = (HASH_NODE_S *)NULL;
    HASH_NODE_S pstLoopNode = NULL;
    ULONG ulIndex = 0;

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    pstList = HASH_GET_LIST(pstTbl, ulIndex);

    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstLoopNode)
    {
        if (pfKeyCmp(pstLoopNode, pKey))
        {
            break;
        }
        pstPrevNode = pstLoopNode;
    }
    
    HASH_ListAddAfter(pstList, pstPrevNode, pstNode);
    return;
}
void HASH_Del(HASH_NODE_S *pstNode)
{
    HLIST_Del(pstNode);
}

void HASH_Free(HASH_NODE_S *pstNode)
{
    free(pstNode);
}

/* pfFree = HASH_FreeNode */
void HASH_Destroy(HASH_TABLE_S *pstTbl, void (*pfFree)(HASH_NODE_S*))                        
{
    HASH_NODE_S *pstNode = NULL;
    HASH_NODE_S *pstNext = NULL;
    ULONG ulIndex = 0;

    if (NULL == pstTbl)
    {
        return ;
    }

    if (NULL != pfFree)
    {
        HASH_TBL_FOREACH(pstTbl, ulIndex)
        {
            /* safe to del */
            HASH_SCAN_BUCKT_SAFE(pstTbl, ulIndex, pstNode, pstNext)
            {
                (*pfFree)(pstNode);
            }
        }
    }

    free((HASH_TABLE_S *)pstTbl);

    return;
}


HASH_NODE_S * HASH_Find(HASH_TABLE_S *pstTbl, const void * pKey,
                            LONG (*pfKeyCmp)(const HASH_NODE_S *, const void *))
{
    HASH_NODE_S *pstNode = NULL;
    ULONG ulIndex = 0;

    if ((NULL == pstTbl) || (NULL == pKey) || (NULL == pfKeyCmp))
    {
        return (HASH_NODE_S *)NULL;
    }

    ulIndex = HASH_GET_INDEX(pstTbl, pKey);
    if (!HASH_IS_VALID_INDEX(pstTbl, ulIndex))
    {
        return (HASH_NODE_S *)NULL;
    }

    HASH_SCAN_BUCKT(pstTbl, ulIndex, pstNode)
    {
        if (0 == pfKeyCmp(pstNode, pKey))
        {
            break;
        }
    }
    
    return pstNode;
}

#ifndef __cplusplus
}
#endif  // end of__cplusplus


参考文档:

https://blog.csdn.net/zhanglei4214/article/details/6767288

http://blog.chinaunix.net/uid-28458801-id-4276934.html

猜你喜欢

转载自blog.csdn.net/qq_33195791/article/details/81347292