0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

uthash简介及使用说明

FPGA之家 来源:嵌入式与Linux那些事 作者:嵌入式那些事 2022-08-27 18:04 次阅读

  • 1. uthash简介

  • 2. uthash的使用

    • 2.1 定义结构体

    • 2.2 添加

    • 2.3 查找

    • 2.4 替换

    • 2.5 删除

    • 2.6 循环删除

    • 2.7 删除哈希表所有元素

    • 2.8 计算哈希表元素个数

    • 2.9 遍历哈希表中的所有项目

    • 2.10 排序哈希表

    • 2.11 完整代码

  • 3. 键值的各种类型举例

    • 3.1 整型键值

    • 3.2 字符串键值

    • 3.3 指针键值

    • 3.4 结构体键值

  • 4. 常用宏参考

    • 4.1 类型宏

    • 4.2 通用宏

    • 4.4 参数说明

1. uthash简介

  由于C语言本身不存在哈希,但是当需要使用哈希表的时候自己构建哈希会异常复杂。因此,我们可以调用开源的第三方头文件,这只是一个头文件:uthash.h。我们需要做的就是将头文件复制到项目中,然后:#include "uthash.h"。由于uthash仅是头文件,因此没有可链接的库代码。

  使用uthash添加,查找和删除通常是常数时间的操作,此哈希的目标是简约高效,大约有1000行代码。

  uthash还包括三个额外的头文件,主要提供链表,动态数组和字符串。utlist.h为C结构提供了链接列表宏。utarray.h使用宏实现动态数组。utstring.h实现基本的动态字符串。

  github下载链接:https://github.com/troydhanson/uthash

2. uthash的使用

2.1 定义结构体

  这里我们将id作为一个索引值,也就是键值,将name作为value。

#include"uthash.h"
structmy_struct{
intid;/*键值*/
charname[10];
UT_hash_handlehh;/*使能哈希表*/
};
structmy_struct*users=NULL;//*声明哈希为NULL指针*/

  注意:一定要包含UT_hash_handle hh;hh不需要初始化。它可以命名为任何名称,但是我们一般都命名为hh。

2.2 添加

  HASH_ADD_INT表示添加的键值为int类型。

  HASH_ADD_STR表示添加的键值为字符串类型。

  HASH_ADD_PTR表示添加的键值为指针类型。

  HASH_ADD表示添加的键值可以是任意类型。

voidadd_user(intuser_id,char*name){
structmy_struct*s;
HASH_FIND_INT(users,&user_id,s);/*重复性检查,当把两个相同key值的结构体添加到哈希表中时会报错*/
if(s==NULL){
s=(structmy_struct*)malloc(sizeof*s);///*只有在哈希中不存在ID的情况下,我们才创建该项目并将其添加。否则,我们只修改已经存在的结构。*/
s->id=user_id;
HASH_ADD_INT(users,id,s);
}
strcpy(s->name,name);
}

  HASH_ADD_INT函数中,第一个参数users是哈希表,第二个参数id是键字段的名称。最后一个参数s是指向要添加的结构的指针。

2.3 查找

structmy_struct*find_user(intuser_id){
structmy_struct*s;
s=(structmy_struct*)malloc(sizeof*s);
HASH_FIND_INT(users,&user_id,s);/*s:返回值*/
returns;
}

  在上述代码中,第一个参数users是哈希表,第二个参数是user_id的地址一定要传递地址)。最后s是输出变量。当可以在哈希表中找到相应键值时,s返回给定键的结构,当找不到时s返回NULL。

2.4 替换

  HASH_REPLACE宏等效于HASH_ADD宏,HASH_REPLACE会尝试查找和删除项目外。如果找到并删除了一个项目,它还将返回该项目的指针作为输出参数。

voidreplace_user(HashHead*head,HashNode*newNode){
HashNode*oldNode=find_user(*head,newNode->id);
if(oldNode)
HASH_REPLACE_INT(*head,id,newNode,oldNode);
}

2.5 删除

  要从哈希表中删除结构,必须具有指向它的指针。(如果只有键,请先执行HASH_FIND以获取结构指针)。

voiddelete_user(structmy_struct*user){
HASH_DEL(users,user);/*user:将要删除的结构体指针*/
free(user);
}

  同样,这里users是哈希表,user是指向我们要从哈希中删除的结构的指针。

  删除结构只是将其从哈希表中删除,并非free 。何时释放结构的选择完全取决于自己;uthash永远不会主动释放结构。

2.6 循环删除

  HASH_ITER是一个宏定义,程序执行时被替换为一个循环。

voiddelete_all(){
structmy_struct*current_user,*tmp;

HASH_ITER(hh,users,current_user,tmp){
HASH_DEL(users,current_user);
free(current_user);
}
}

2.7 删除哈希表所有元素

  如果只想删除所有项目,但不释放它们或进行每个元素的清理,则可以通过一次操作更有效地做到这一点:

HASH_CLEAR(hh,users);

  之后,列表头(此处为users)将设置为NULL。

2.8 计算哈希表元素个数

unsignedintnum_users;
num_users=HASH_COUNT(users);
printf("thereare%uusers
",num_users);

  当users为NULL时,HASH_COUNT会返回0。

2.9 遍历哈希表中的所有项目

voidprint_users(){
structmy_struct*s;
for(s=users;s!=NULL;s=s->hh.next){
printf("userid%d:name%s
",s->id,s->name);
}
}

  还有一个hh.prev指针,可用于从任何已知项开始向后迭代哈希。

  由于hh.prev和hh.next字段的缘故,可以在哈希中向前和向后迭代。可以通过遍历这些指针来访问哈希中的所有项目,因此哈希也是双链表

2.10 排序哈希表

HASH_SORT(users,name_sort);

  第二个参数是指向比较函数的指针。它必须接受两个指针参数(要比较的项目),并且如果第一个项目分别在第二个项目之前,等于或之后排序,则必须返回小于零,零或大于零的int。 (这与标准C库中的strcmp或qsort使用的方法相同)。

intsort_function(void*a,void*b){
/*将a与b比较*/
if(a< b) return(int)-1;
if(a==b)return(int)0;
if(a>b)return(int)1;

}

  name_sort和id_sort的两个排序函数示例。

intname_sort(structmy_struct*a,structmy_struct*b){
returnstrcmp(a->name,b->name);
}

intid_sort(structmy_struct*a,structmy_struct*b){
return(a->id-b->id);
}

voidsort_by_name(){
HASH_SORT(users,name_sort);
}

voidsort_by_id(){
HASH_SORT(users,id_sort);
}

2.11 完整代码

/*
*@Description:UTHASH的使用
*@Version:V1.0
*@Autor:公众号【嵌入式Linux那些事】
*@Date:2020-2-22112
*@LastEditors:公众号【嵌入式与Linux那些事】
*@LastEditTime:2020-2-22246
*/
#include/*gets*/
#include/*atoi,malloc*/
#include/*strcpy*/
#include"uthash.h"

structmy_struct{
intid;/*键值*/
charname[10];
UT_hash_handlehh;/*使能结构体*/
};

structmy_struct*users=NULL;

voidadd_user(intuser_id,char*name){
structmy_struct*s;

HASH_FIND_INT(users,&user_id,s);
if(s==NULL){
s=(structmy_struct*)malloc(sizeof*s);
s->id=user_id;
HASH_ADD_INT(users,id,s);
}
strcpy(s->name,name);
}

structmy_struct*find_user(intuser_id){
structmy_struct*s;
s=(structmy_struct*)malloc(sizeof*s);
HASH_FIND_INT(users,&user_id,s);
returns;
}

voiddelete_user(structmy_struct*user){
HASH_DEL(users,user);
free(user);
}

voiddelete_all(){
structmy_struct*current_user,*tmp;

HASH_ITER(hh,users,current_user,tmp){
HASH_DEL(users,current_user);
free(current_user);
}
}

voidprint_users(){
structmy_struct*s;

for(s=users;s!=NULL;s=(structmy_struct*)(s->hh.next)){
printf("userid%d:name%s
",s->id,s->name);
}
}

intname_sort(structmy_struct*a,structmy_struct*b){
returnstrcmp(a->name,b->name);
}

intid_sort(structmy_struct*a,structmy_struct*b){
return(a->id-b->id);
}

voidsort_by_name(){
HASH_SORT(users,name_sort);
}

voidsort_by_id(){
HASH_SORT(users,id_sort);
}

intmain(intargc,char*argv[]){
charin[10];
intid=1,running=1;
structmy_struct*s;
unsignednum_users;

while(running){
printf("1.adduser
");
printf("2.add/renameuserbyid
");
printf("3.finduser
");
printf("4.deleteuser
");
printf("5.deleteallusers
");
printf("6.sortitemsbyname
");
printf("7.sortitemsbyid
");
printf("8.printusers
");
printf("9.countusers
");
printf("10.quit
");
gets(in);
switch(atoi(in)){
case1:
printf("name?
");
add_user(id++,gets(in));
break;
case2:
printf("id?
");
gets(in);id=atoi(in);
printf("name?
");
add_user(id,gets(in));
break;
case3:
printf("id?
");
s=find_user(atoi(gets(in)));
printf("user:%s
",s?s->name:"unknown");
break;
case4:
printf("id?
");
s=find_user(atoi(gets(in)));
if(s)delete_user(s);
elseprintf("idunknown
");
break;
case5:
delete_all();
break;
case6:
sort_by_name();
break;
case7:
sort_by_id();
break;
case8:
print_users();
break;
case9:
num_users=HASH_COUNT(users);
printf("thereare%uusers
",num_users);
break;
case10:
running=0;
break;
}
}

delete_all();
return0;
}

3. 键值的各种类型举例

3.1 整型键值

  当键值为整型时,可以使用HASH_ADD_INT和HASH_FIND_INT。(对于所有类型的键,其他操作(例如HASH_DELETE和)HASH_SORT都是相同的)。

3.2 字符串键值

  当键值为字符串时,具体要使用那个函数取决于结构体中的键值为字符串数组还是字符串指针。 这一点很重要。当结构体中的键值为字符串数组时,使用HASH_ADD_STR。键值为字符串指针时使用HASH_ADD_KEYPTR。接下来给出两个例子参考。

  当结构体中的键值为字符串数组时

#include/*strcpy*/
#include/*malloc*/
#include/*printf*/
#include"uthash.h"

structmy_struct{
charname[10];
intid;
UT_hash_handlehh;
};


intmain(intargc,char*argv[]){
constchar*names[]={"joe","bob","betty",NULL};
structmy_struct*s,*tmp,*users=NULL;

for(inti=0;names[i];++i){
s=(structmy_struct*)malloc(sizeof*s);
strcpy(s->name,names[i]);
s->id=i;
HASH_ADD_STR(users,name,s);
}

HASH_FIND_STR(users,"betty",s);
if(s)printf("betty'sidis%d
",s->id);


HASH_ITER(hh,users,s,tmp){
HASH_DEL(users,s);
free(s);
}
return0;
}

  当结构体中的键值为字符串指针时

#include/*strcpy*/
#include/*malloc*/
#include/*printf*/
#include"uthash.h"

structmy_struct{
constchar*name;
intid;
UT_hash_handlehh;
};


intmain(intargc,char*argv[]){
constchar*names[]={"joe","bob","betty",NULL};
structmy_struct*s,*tmp,*users=NULL;

for(inti=0;names[i];++i){
s=(structmy_struct*)malloc(sizeof*s);
s->name=names[i];
s->id=i;
HASH_ADD_KEYPTR(hh,users,s->name,strlen(s->name),s);
}

HASH_FIND_STR(users,"betty",s);
if(s)printf("betty'sidis%d
",s->id);

HASH_ITER(hh,users,s,tmp){
HASH_DEL(users,s);
free(s);
}
return0;
}

3.3 指针键值

#include
#include
#include"uthash.h"

typedefstruct{
void*key;
inti;
UT_hash_handlehh;
}el_t;

el_t*hash=NULL;
char*someaddr=NULL;

intmain(){
el_t*d;
el_t*e=(el_t*)malloc(sizeof*e);
if(!e)return-1;
e->key=(void*)someaddr;
e->i=1;
HASH_ADD_PTR(hash,key,e);
HASH_FIND_PTR(hash,&someaddr,d);
if(d)printf("found
");

/*releasememory*/
HASH_DEL(hash,e);
free(e);
return0;
}

3.4 结构体键值

  在将项目添加到哈希或查找项目之前,必须将结构体键值中的元素清零。

#include
#include
#include"uthash.h"

typedefstruct{
chara;
intb;
}record_key_t;

typedefstruct{
record_key_tkey;
UT_hash_handlehh;
}record_t;

intmain(intargc,char*argv[]){
record_tl,*p,*r,*tmp,*records=NULL;

r=(record_t*)malloc(sizeof*r);

memset(r,0,sizeof*r);/*结构体键值清零*/
r->key.a='a';
r->key.b=1;
HASH_ADD(hh,records,key,sizeof(record_key_t),r);

memset(&l,0,sizeof(record_t));
l.key.a='a';
l.key.b=1;
HASH_FIND(hh,records,&l.key,sizeof(record_key_t),p);

if(p)printf("found%c%d
",p->key.a,p->key.b);

HASH_ITER(hh,records,p,tmp){
HASH_DEL(records,p);
free(p);
}
return0;
}

4. 常用宏参考

4.1 类型宏

HASH_ADD_INT(head,keyfield_name,item_ptr)

HASH_REPLACE_INT(head,keyfiled_name,item_ptr,replaced_item_ptr)

HASH_FIND_INT(head,key_ptr,item_ptr)

HASH_ADD_STR(head,keyfield_name,item_ptr)

HASH_REPLACE_STR(head,keyfield_name,item_ptr,replaced_item_ptr)

HASH_FIND_STR(head,key_ptr,item_ptr)

HASH_ADD_PTR(head,keyfield_name,item_ptr)

HASH_REPLACE_PTR(head,keyfield_name,item_ptr,replaced_item_ptr)

HASH_FIND_PTR(head,key_ptr,item_ptr)

HASH_DEL(head,item_ptr)

HASH_SORT(head,cmp)

HASH_COUNT(head)

4.2 通用宏

HASH_ADD(hh_name,head,keyfield_name,key_len,item_ptr)

HASH_ADD_BYHASHVALUE(hh_name,head,keyfield_name,key_len,hashv,item_ptr)

HASH_ADD_KEYPTR(hh_name,head,key_ptr,key_len,item_ptr)

HASH_ADD_KEYPTR_BYHASHVALUE(hh_name,head,key_ptr,key_len,hashv,item_ptr)

HASH_ADD_INORDER(hh_name,head,keyfield_name,key_len,item_ptr,cmp)

HASH_ADD_BYHASHVALUE_INORDER(hh_name,head,keyfield_name,key_len,hashv,item_ptr,cmp)

HASH_ADD_KEYPTR_INORDER(hh_name,head,key_ptr,key_len,item_ptr,cmp)

HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh_name,head,key_ptr,key_len,hashv,item_ptr,cmp)

HASH_REPLACE(hh_name,head,keyfield_name,key_len,item_ptr,replaced_item_ptr)

HASH_REPLACE_BYHASHVALUE(hh_name,head,keyfield_name,key_len,hashv,item_ptr,replaced_item_ptr)

HASH_REPLACE_INORDER(hh_name,head,keyfield_name,key_len,item_ptr,replaced_item_ptr,cmp)

HASH_REPLACE_BYHASHVALUE_INORDER(hh_name,head,keyfield_name,key_len,hashv,item_ptr,replaced_item_ptr,cmp)

HASH_FIND(hh_name,head,key_ptr,key_len,item_ptr)

HASH_FIND_BYHASHVALUE(hh_name,head,key_ptr,key_len,hashv,item_ptr)

HASH_DELETE(hh_name,head,item_ptr)

HASH_VALUE(key_ptr,key_len,hashv)

HASH_SRT(hh_name,head,cmp)

HASH_CNT(hh_name,head)

HASH_CLEAR(hh_name,head)

HASH_SELECT(dst_hh_name,dst_head,src_hh_name,src_head,condition)

HASH_ITER(hh_name,head,item_ptr,tmp_item_ptr)

HASH_OVERHEAD(hh_name,head)

4.4 参数说明

  hh_name:UT_hash_handle结构中字段的 名称。俗称 hh。

  head:结构指针变量,用作哈希的“头”。如此命名是因为它最初指向添加到哈希中的第一项。

  keyfield_name:结构中键字段的名称。(对于多字段键,这是键的第一个字段)。

  key_len:键字段的长度(以字节为单位)。例如,对于整数键,它是sizeof(int),而对于字符串键,它是strlen(key)。

  key_ptr:对于HASH_FIND,这是指向要在哈希中查找的键的指针(由于它是指针,因此不能在此处直接传递文字值)。对于 HASH_ADD_KEYPTR,这是要添加的项的键的地址。

  hashv:提供的键的哈希值。这是BYHASHVALUE宏的输入参数。如果要重复查找相同的键,则重用缓存的哈希值可以优化性能。

  item_ptr:指向要添加,删除,替换或查找的结构的指针,或迭代期间的当前指针。这是HASH_ADD, HASH_DELETE和HASH_REPLACE宏的输入参数,输出参数为HASH_FIND 和HASH_ITER。(当HASH_ITER用于迭代时,tmp_item_ptr 是与item_ptr内部使用的类型相同的另一个变量)。

  replace_item_ptr:用于HASH_REPLACE宏。这是一个输出参数,设置为指向替换的项目(如果没有替换的项目,则设置为NULL)。

  cmp:指向比较函数的指针,该函数接受两个参数(指向要比较的项目的指针),并返回一个int值,该值指定第一个项目应在第二个项目之前,等于还是之后排序(如strcmp)。

  condition:接受单个参数的函数或宏(指向结构的空指针,需要将其强制转换为适当的结构类型)。如果应“选择”结构以将其添加到目标哈希中,则函数或宏的值应为非零值。

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7604

    浏览量

    136788
  • 代码
    +关注

    关注

    30

    文章

    4787

    浏览量

    68589
  • 哈希表
    +关注

    关注

    0

    文章

    9

    浏览量

    4841

原文标题:你知道uthash吗?

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    PCBNavigator的使用说明

    PCBNavigator的使用说明 
    发表于 05-11 20:46

    iccavr使用说明

    iccavr使用说明
    发表于 04-06 11:46

    PCBNavigator的使用说明

    PCBNavigator的使用说明
    发表于 08-20 16:02

    NI6008使用说明

    NI6008的使用说明
    发表于 08-31 16:11

    常用的API及使用说明

    为了方便用户使用,这里列出了常用的API,并给出了相关的使用说明
    发表于 03-30 06:20

    步进电机模块使用说明

    FPGA控制_步进电机模块使用说明今天给大侠带来步进电机模块使用说明,话不多说,上货。一、步进电机简介步进电机是将电脉冲信号转变为角位移或线位移的开环控制电机,是现代数字程序控制系统中的主要执行元件
    发表于 07-07 07:57

    PCA9685使用说明

    舵机驱动板,网上很多资源,但是基于STM32F103的能用代码实在太少。具体使用说明我觉得这两个链接写的够详细了,附上链接1.PCA9685使用说明12.PCA9685使用说明2文字说明
    发表于 08-11 06:03

    诺基亚Nokia 770手机使用说明

    诺基亚Nokia 770手机使用说明简介 注意事项 操作说明 设置 .........
    发表于 09-01 09:57 5次下载

    飞利浦X510手机使用说明

    飞利浦X510手机使用说明简介 使用说明 设置 ...............
    发表于 09-02 11:44 8次下载

    步步高i518手机使用说明

    步步高i518手机使用说明简介 使用说明 ..............
    发表于 09-03 16:24 61次下载

    步步高K12手机使用说明

    步步高K12手机使用说明简介 使用说明 操作说明 ...........
    发表于 09-03 16:25 12次下载

    步步高K118手机使用说明

    步步高K118手机使用说明简介 安全事项 使用说明 ............
    发表于 09-03 16:25 32次下载

    三星SGH-E418手机使用说明

    三星SGH-E418手机使用说明简介 使用说明 操作 ..........
    发表于 09-07 12:07 10次下载

    C语言uthash简介的使用

    参考 4.1 类型宏 4.2 通用宏 4.4 参数说明 1. uthash简介  由于C语言本身不存在哈希,但是当需要使用哈希表的时候自己构建哈希会异常复杂。因此,我们可以调用开源的第三方
    的头像 发表于 03-22 10:44 8448次阅读

    经纬仪简介使用说明

    经纬仪简介使用说明
    发表于 01-14 11:01 2次下载