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

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

3天内不再提示

C语言uthash简介的使用

FPGA之家 来源:嵌入式那些事嵌入式与L 作者:嵌入式与Linux那些 2021-03-22 10:44 次阅读

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实现基本的动态字符串。

2. uthash的使用2.1 定义结构体

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

#include “uthash.h”

struct my_struct {

int id; /* 键值 */

char name[10];

UT_hash_handle hh; /* 使能哈希表 */

};

struct my_struct *users = NULL; //*声明哈希为NULL指针*/

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

2.2 添加

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

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

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

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

void add_user(int user_id, char *name) {

struct my_struct *s;

HASH_FIND_INT(users, &user_id, s); /*重复性检查,当把两个相同key值的结构体添加到哈希表中时会报错*/

if (s==NULL) {

s = (struct my_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 查找

struct my_struct *find_user(int user_id) {

struct my_struct *s;

s = (struct my_struct *)malloc(sizeof *s);

HASH_FIND_INT( users, &user_id, s ); /* s: 返回值 */

return s;

}

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

2.4 替换

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

void replace_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以获取结构指针)。

void delete_user(struct my_struct *user) {

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

free(user);

}

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

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

2.6 循环删除

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

void delete_all() {

struct my_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 计算哈希表元素个数

unsigned int num_users;

num_users = HASH_COUNT(users);

printf(“there are %u users

”, num_users);

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

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

void print_users() {

struct my_struct *s;

for(s=users; s != NULL; s=s-》hh.next) {

printf(“user id %d: name %s

”, s-》id, s-》name);

}

}

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

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

2.10 排序哈希表

HASH_SORT( users, name_sort );

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

int sort_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的两个排序函数示例。

int name_sort(struct my_struct *a, struct my_struct *b) {

return strcmp(a-》name,b-》name);

}

int id_sort(struct my_struct *a, struct my_struct *b) {

return (a-》id - b-》id);

}

void sort_by_name() {

HASH_SORT(users, name_sort);

}

void sort_by_id() {

HASH_SORT(users, id_sort);

}

2.11 完整代码

/*

* @Description: UTHASH的使用

* @Version: V1.0

* @Autor: 公众号【嵌入式Linux那些事】

* @Date: 2020-2-2 2112

* @LastEditors: 公众号【嵌入式与Linux那些事】

* @LastEditTime: 2020-2-2 2246

*/

#include 《stdio.h》 /* gets */

#include 《stdlib.h》 /* atoi, malloc */

#include 《string.h》 /* strcpy */

#include “uthash.h”

struct my_struct {

int id; /* 键值 */

char name[10];

UT_hash_handle hh; /* 使能结构体 */

};

struct my_struct *users = NULL;

void add_user(int user_id, char *name) {

struct my_struct *s;

HASH_FIND_INT(users, &user_id, s);

if (s==NULL) {

s = (struct my_struct *)malloc(sizeof *s);

s-》id = user_id;

HASH_ADD_INT( users, id, s );

}

strcpy(s-》name, name);

}

struct my_struct *find_user(int user_id) {

struct my_struct *s;

s = (struct my_struct *)malloc(sizeof *s);

HASH_FIND_INT( users, &user_id, s );

return s;

}

void delete_user(struct my_struct *user) {

HASH_DEL(users, user);

free(user);

}

void delete_all() {

struct my_struct *current_user, *tmp;

HASH_ITER(hh, users, current_user, tmp) {

HASH_DEL(users, current_user);

free(current_user);

}

}

void print_users() {

struct my_struct *s;

for(s=users; s != NULL; s=(struct my_struct*)(s-》hh.next)) {

printf(“user id %d: name %s

”, s-》id, s-》name);

}

}

int name_sort(struct my_struct *a, struct my_struct *b) {

return strcmp(a-》name,b-》name);

}

int id_sort(struct my_struct *a, struct my_struct *b) {

return (a-》id - b-》id);

}

void sort_by_name() {

HASH_SORT(users, name_sort);

}

void sort_by_id() {

HASH_SORT(users, id_sort);

}

int main(int argc, char *argv[]) {

char in[10];

int id=1, running=1;

struct my_struct *s;

unsigned num_users;

while (running) {

printf(“ 1. add user

”);

printf(“ 2. add/rename user by id

”);

printf(“ 3. find user

”);

printf(“ 4. delete user

”);

printf(“ 5. delete all users

”);

printf(“ 6. sort items by name

”);

printf(“ 7. sort items by id

”);

printf(“ 8. print users

”);

printf(“ 9. count users

”);

printf(“10. quit

”);

gets(in);

switch(atoi(in)) {

case 1:

printf(“name?

”);

add_user(id++, gets(in));

break;

case 2:

printf(“id?

”);

gets(in); id = atoi(in);

printf(“name?

”);

add_user(id, gets(in));

break;

case 3:

printf(“id?

”);

s = find_user(atoi(gets(in)));

printf(“user: %s

”, s ? s-》name : “unknown”);

break;

case 4:

printf(“id?

”);

s = find_user(atoi(gets(in)));

if (s) delete_user(s);

else printf(“id unknown

”);

break;

case 5:

delete_all();

break;

case 6:

sort_by_name();

break;

case 7:

sort_by_id();

break;

case 8:

print_users();

break;

case 9:

num_users=HASH_COUNT(users);

printf(“there are %u users

”, num_users);

break;

case 10:

running=0;

break;

}

}

delete_all();

return 0;

}

3. 键值的各种类型举例3.1 整型键值

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

3.2 字符串键值

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

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

#include 《string.h》 /* strcpy */

#include 《stdlib.h》 /* malloc */

#include 《stdio.h》 /* printf */

#include “uthash.h”

struct my_struct {

char name[10];

int id;

UT_hash_handle hh;

};

int main(int argc, char *argv[]) {

const char *names[] = { “joe”, “bob”, “betty”, NULL };

struct my_struct *s, *tmp, *users = NULL;

for (int i = 0; names[i]; ++i) {

s = (struct my_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‘s id is %d

”, s-》id);

HASH_ITER(hh, users, s, tmp) {

HASH_DEL(users, s);

free(s);

}

return 0;

}

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

#include 《string.h》 /* strcpy */

#include 《stdlib.h》 /* malloc */

#include 《stdio.h》 /* printf */

#include “uthash.h”

struct my_struct {

const char *name;

int id;

UT_hash_handle hh;

};

int main(int argc, char *argv[]) {

const char *names[] = { “joe”, “bob”, “betty”, NULL };

struct my_struct *s, *tmp, *users = NULL;

for (int i = 0; names[i]; ++i) {

s = (struct my_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’s id is %d

”, s-》id);

HASH_ITER(hh, users, s, tmp) {

HASH_DEL(users, s);

free(s);

}

return 0;

}

3.3 指针键值

#include 《stdio.h》

#include 《stdlib.h》

#include “uthash.h”

typedef struct {

void *key;

int i;

UT_hash_handle hh;

} el_t;

el_t *hash = NULL;

char *someaddr = NULL;

int main() {

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

”);

/* release memory */

HASH_DEL(hash,e);

free(e);

return 0;

}

3.4 结构体键值

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

#include 《stdlib.h》

#include 《stdio.h》

#include “uthash.h”

typedef struct {

char a;

int b;

} record_key_t;

typedef struct {

record_key_t key;

UT_hash_handle hh;

} record_t;

int main(int argc, char *argv[]) {

record_t l, *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);

}

return 0;

}

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

原文标题:你知道uthash吗?

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

责任编辑:haq

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

    关注

    4981

    文章

    18281

    浏览量

    288401
  • C语言
    +关注

    关注

    180

    文章

    7530

    浏览量

    128529

原文标题:你知道uthash吗?

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

收藏 人收藏

    评论

    相关推荐

    汇编指令是什么 计算机语言汇编指令简介

    汇编指令简介 汇编语言是计算机语言的一种,是一种低级语言。相比高级语言,汇编语言更接近底层硬件,
    的头像 发表于 12-13 11:18 449次阅读
    汇编指令是什么 计算机<b class='flag-5'>语言</b>汇编指令<b class='flag-5'>简介</b>

    基于检索的大语言模型简介

    简介章节讲的是比较基础的,主要介绍了本次要介绍的概念,即检索(Retrieval)和大语言模型(LLM)
    的头像 发表于 11-15 14:50 352次阅读
    基于检索的大<b class='flag-5'>语言</b>模型<b class='flag-5'>简介</b>

    C语言怎样处理json文件?

    获取到的JSON文件,怎样通过C语言进行处理,因为单片机里面只能用C语言,有没有C语言处理起来比
    发表于 11-01 06:16

    C语言经典算法大全

    C语言经典算法,详细解析算法过程及算法思想,给读者具有启发意义,教程包含C语言大部分常用算法,仅供学习,禁止商业传播
    发表于 10-07 08:16

    C语言深度解析

    C语言深度解析,本资料来源于网络,对C语言的学习有很大的帮助,有着较为深刻的解析,可能会对读者有一定的帮助。
    发表于 09-28 07:00

    Linux下C语言编程入门教程

    u3000本文是Linux 下C 语言编程入门教程。主要介绍了Linux 的发展与特点、C语言的基础知识、Linux 程序设计基础知识及其下C
    发表于 09-22 06:56

    C语言基础知识(一)

    、不等、大于、小于等。逻辑运算符:用于执行逻辑操作,如与、或、非等。赋值运算符:用于将值赋给变量。自增自减运算符:用于在原始值的基础上增加或减少1。C语言是一种高级的通用编程语言,广泛应用于系统软件
    发表于 08-10 15:16

    C语言基础知识(一)

    、不等、大于、小于等。逻辑运算符:用于执行逻辑操作,如与、或、非等。赋值运算符:用于将值赋给变量。自增自减运算符:用于在原始值的基础上增加或减少1。C语言是一种高级的通用编程语言,广泛应用于系统软件
    发表于 08-07 16:51

    C语言面试必备30问

    C语言
    YS YYDS
    发布于 :2023年06月21日 00:30:13

    C语言基础知识(1)--简介

    C语言能以简易的方式编译、处理低级存储器。
    的头像 发表于 06-08 16:41 1490次阅读
    C<b class='flag-5'>语言</b>基础知识(1)--<b class='flag-5'>简介</b>

    链表(2)#C语言程序设计进阶

    C语言
    学习硬声知识
    发布于 :2023年05月31日 15:47:19

    链表(1)#C语言程序设计进阶

    C语言
    学习硬声知识
    发布于 :2023年05月31日 15:46:50

    联合(2)#C语言程序设计进阶

    C语言
    学习硬声知识
    发布于 :2023年05月31日 15:46:21