uthash

在软件开发中,不可不免的会使用到hash表,hash表的优点这里就不说了,以下介绍一个hash表的C实现,

uthash是用宏实现的,使用的时候非常方便,只用包含uthash.h即可。

Uthash的三个数据结构:

1. typedef struct UT_hash_bucket {

   struct UT_hash_handle *hh_head;

   unsigned count;

   unsigned expand_mult;

} UT_hash_bucket;

UT_hash_bucket作用提供根据hash进行索引。

2.typedef struct UT_hash_table {

   UT_hash_bucket *buckets;

   unsigned num_buckets, log2_num_buckets;

   unsigned num_items;

   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */

   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ 

   unsigned ideal_chain_maxlen;

   unsigned nonideal_items;             

   unsigned ineff_expands, noexpand;

   uint32_t signature; /* used only to find hash tables in external analysis */

#ifdef HASH_BLOOM

   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */

   uint8_t *bloom_bv;

   char bloom_nbits;

#endif

} UT_hash_table;

UT_hash_table可以看做hash表的表头。

3. typedef struct UT_hash_handle {

   struct UT_hash_table *tbl;

   void *prev;                       /* prev element in app order      */

   void *next;                       /* next element in app order      */

   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */

   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */

   void *key;                        /* ptr to enclosing struct's key  */

   unsigned keylen;                  /* enclosing struct's key len     */

   unsigned hashv;                   /* result of hash-fcn(key)        */

} UT_hash_handle;

UT_hash_handle,用户自定义数据必须包含的结构。

三种数据结构的关系如下:

说明:

每一个节点(用户自定义的)必须包含一个UT_hash_handle hh

key:用户自定义,可以是int, string和指针。

hh_prev: 指向前一个UT_hash_handle 

hh_next: 指向下一个UT_hash_handle

hashv:根据key计算出的hash

prev: 指向前一个数据节点(Hash冲突时)

next: 指向下一个数据节点(Hash冲突时)

hho: 数据节点中hh于用户节点首地址的差。

--------------------------------------------我是分割线---------------------------------------

二、uthash的基本用法

由于C语言中,并没有对hash表这类的高级数据结构进行支持,即使在目前通用的C++中,也只支持栈、队列等几个数据结构,对于map,其实是以树结构来实现的,而不是以hash表实现。

uthash是一个C语言的hash表实现。它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与关键类型无关的优点。

uthash使用起来十分方便,只要将头文件uthash.h包含进去就可以使用。

目前,uthash的最新版本(1.9)支持如下平台:

  • Linux
  •   Mac OS X
  • Windows using vs2008 and vs 2010
  • Solaris
  • OpenBSD

 

      通常这足够通用了。唯一的不足是在windows下,在uthash这旧版本中,并不支持vs2008和2010,而是支持MinGW。

 uthash支持:增加、查找、删除、计数、迭代、排序、选择等操作。

第一步:包含头文件

[c-sharp] view plaincopy
  1. #include "uthash.h"  
 

第二步:自定义数据结构。每个结构代表一个键-值对,并且,每个结构中要有一个UT_hash_handle成员。

[c-sharp] view plaincopy
  1. struct my_struct {  
  2. int id; /* key */  
  3. char name[10];  
  4. UT_hash_handle hh; /* makes this structure hashable */  
  5. };  
 

第三步:定义hash表指针。这个指针为前面自定义数据结构的指针,并初始化为NULL。

[c-sharp] view plaincopy
  1. struct my_struct *users = NULL; /* important! initialize to NULL */  
 

第四步:进行一般的操作。

增加:

[c-sharp] view plaincopy
  1. int add_user(int user_id, char *name) {  
  2. struct my_struct *s;  
  3. s = malloc(sizeof(struct my_struct));  
  4. s->id = user_id;  
  5. strcpy(s->name, name);  
  6. HASH_ADD_INT( users, id, s ); /* id: name of key field */  
  7. }  
 

查找:

[c-sharp] view plaincopy
  1. struct my_struct *find_user(int user_id) {  
  2. struct my_struct *s;  
  3. HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */  
  4. return s;  
  5. }  
 

删除:

[c-sharp] view plaincopy
  1. void delete_user(struct my_struct *user) {  
  2. HASH_DEL( users, user); /* user: pointer to deletee */  
  3. free(user); /* optional; it’s up to you! */  
  4. }  
 

计数:

[c-sharp] view plaincopy
  1. unsigned int num_users;  
  2. num_users = HASH_COUNT(users);  
  3. printf("there are %u users/n", num_users);  
 

迭代:

[c-sharp] view plaincopy
  1. void print_users() {  
  2. struct my_struct *s;  
  3. for(s=users; s != NULL; s=s->hh.next) {  
  4. printf("user id %d: name %s/n", s->id, s->name  
  5. }  
  6. }  
 

排序:

[c-sharp] view plaincopy
  1. int name_sort(struct my_struct *a, struct my_struct *b) {  
  2. return strcmp(a->name,b->name);  
  3. }  
  4. int id_sort(struct my_struct *a, struct my_struct *b) {  
  5. return (a->id - b->id);  
  6. }  
  7. void sort_by_name() {  
  8. HASH_SORT(users, name_sort);  
  9. }  
  10. void sort_by_id() {  
  11. HASH_SORT(users, id_sort);  
  12. }  
 

要注意,在uthash中,并不会对其所储存的值进行移动或者复制,也不会进行内存的释放。




--------------------------------------------我是分割线---------------------------------------

uthash使用代码例子

[html] view plaincopy
  1. #include "uthash.h"  
  2. #include <stdlib.h>   /* malloc */  
  3. #include <stdio.h>    /* printf */  
  4. #include <time.h>  
  5.   
  6. typedef struct example_user_t {  
  7.     int id;  
  8.     int cookie;  
  9.     UT_hash_handle hh;  
  10. } example_user_t;  
  11.   
  12. int main(int argc,char *argv[]) {  
  13.     int i;  
  14.     example_user_t *user, *users=NULL;  
  15.   
  16.     srand((unsigned int)time(NULL));  
  17.     /* create elements */  
  18.     for(i=0;i<10;i++) {  
  19.         if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1);  
  20.         user->id = rand()%100;  
  21.         user->cookie = i*i;  
  22.         HASH_ADD_INT(users,id,user);  
  23.     }  
  24.   
  25.     for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) {  
  26.         printf("user %d, cookie %d\n", user->id, user->cookie);  
  27.     }  
  28.    return 0;  


---------------------------------我是分割线-------------------------------------

4. 其他类型key的使用

  本节主要关于key值类型为其他任意类型,例如整型、字符串、指针、结构体等时的用法。

  注意:在使用key值为浮点类型时,由于浮点类型的比较受到精度的影响,例如:1.0000000002被认为与1相等,这些问题在uthash中也存在。

  4.1. int类型key

  前面就是以int类型的key作为示例,总结int类型key使用方法,可以看到其查找和插入分别使用专用接口:HASH_FIND_INT和ASH_ADD_INT。

  4.2. 字符指针char*类型key与字符数组char key[100]类型key

  特别注意在Strting类型中,uthash对指针char*和字符数组(例如char key[100])做了区分,这两种情况下使用的接口函数时不一样的。在添加的时候,key的类型为指针时使用接口函数HASH_ADD_KEYPTR,key的类型为字符数组时,使用接口函数HASH_ADD_STR,除了添加的接口不一样外,其他的查找、删除、变量等接口函数都是一样的。

  4.3.使用地址作为key

  在uthash中也可使用地址做key进行hash操作,使用地址作为key值时,其类型为void*,这样它就可以支持任意类型的地址了。在使用地址作为key时,插入和查找的专用接口函数为HASH_ADD_PTR和HASH_FIND_PTR,其余接口是一样的。

  4.3.其他非常用类型key

  在uthash中还可使用结构体作为key,甚至可以采用组合的方式让多个值作为key,这些在其官方的网站张均有较详细的使用示例。在使用uthash需要注意以下几点:

  在定义hash结构体时不要忘记定义UT_hash_handle的变量

  需确保key值唯一,如果插入key-value对时,key值已经存在,再插入的时候就会出错。

  不同的key值,其增加和查找调用的接口函数不一样,具体可见第4节。一般情况下,不通类型的key,其插入和查找接口函数是不一样的,删除、遍历、元素统计接口是通用的,特殊情况下,字符数组和字符串作为key值时,其插入接口函数不一样,但是查找接口是一样的

你可能感兴趣的:(linux/unix,c基础,计算机基础,C/C++)