uthash简单使用

本文介绍了uthash库在C语言中的使用,包括如何定义结构体,声明哈希表,添加、替换、查找、删除元素,以及遍历和排序哈希表。uthash支持多种类型的key,如int、字符串、指针和结构体,通过特定宏进行操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

uthash简单使用

摘自uthash User Guide

在uthash中,哈希表由结构体组成,每个结构体代表一个键值关联。结构体中的一个或多个域构成键(key),结构体自身的指针就是值(value)。

  1. key 可以是任何数据类型。因为对于 uthash 来说,它只是一个字节序列(a sequence of bytes)。
  2. 哈希表中,key 必须是唯一的。意味着,在添加元素时必须检查该键是否已经存在。
  3. 针对不同类型的 key ,使用不同的宏来操作。
/*
 * commonly used macros summary here
 */

// int-keyed hash
HASH_ADD_INT(head, intfield, add);
HASH_FIND_INT(head, findint, out);
HASH_REPLACE_INT(head, intfield, add, replaced);

// string-keyed hash (string within structure, i.e. char a[10];)
HASH_ADD_STR(head, strfield, add);
HASH_FIND_STR(head, findstr, out);
HASH_REPLACE_STR(head, strfield, add, replaced);

// string-keyed hash (structure points to string, i.e. char *str;)
HASH_ADD_KEYPTR(hh, head, keyptr, keylen, add);
// ADD, FIND same as above

// pointer-keyed hash (i.e. void *key;)
HASH_ADD_PTR(head, ptrfield, add);
HASH_FIND_PTR(head, findptr, out);
HASH_REPLACE_PTR(head, ptrfield, add, replaced);

// structure-keyed hash
HASH_ADD(hh, head, fieldname, keylen, add);
HASH_FIND(hh, head, keyptr, keylen, out);
HASH_REPLACE(hh, head, fieldname, keylen, add, replaced);

// for all type of key
HASH_COUNT(head);
HASH_ITER(hh, head, el, tmp);
HASH_DEL(head, delptr);

key为int型

定义结构体

#include "uthash.h"

typedef struct my_struct{
    int id;             /* key */
    char name[10];
    UT_hash_handle hh;  /* make this structure hashalbe */
} Hash;

注意

  • 结构体中必须包含 UT_hash_handle。可以任意命名。但如果命名为 hh 则可以使用便利宏(convenience macros)简化添加、查找和删除元素的参数列表。

声明哈希表

Hash *users = NULL;  /* important! initialize to NULL */

重要:必须声明为指向定义结构体的空指针(NULL)。

添加

void add_user(int user_id, char *name) {
    Hash *find;
    
    HASH_FIND_INT(users, &user_id, find);  /* id already in the hash? */
    if (find == NULL) {
        find = (Hash*)malloc(sizeof(Hash));
        find->id = user_id;
        HASH_ADD_INT(users, id, find);       /* id: name of key field */
    }
    strcpy(find->name, name);
}

注意

  • 第二个参数为 key 域的名字
  • 结构体一经添加到哈希表中,不要修改键。如果要实现该操作,需要先删除再添加。

传递哈希指针到函数中

上面的添加示例中,users 是一个全局变量。如果需要传递哈希表到函数中进行操作怎么办?

/* 反例 */
void add_user(Hash *users, int user_id, char *name) {
    ...
    HASH_ADD_INT(users, id, s);
}

/* 正例 */
void add_user(Hash **users, int user_id, char *name) { ...
    ...
    HASH_ADD_INT(*users, id, s);
}

/* 理解 */
void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

理解

  • 原理同普通函数传参,如常用的 swap 交换两个变量的值。添加函数的目的是修改(增加)哈希表,只有传递指针才能达到修改的目的。

替换

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

查找

struct my_struct *find_user(int user_id) {
    Hash *find;

    HASH_FIND_INT(users, &user_id, find);  /* find: output pointer */
    return find;
}

说明

  • users 是哈希表
  • &user_id 指向要查找的键(key)的指针
  • find 存储查找结果。如果存在,find 保存指向包含给定键值的结构体的指针,否则 find 等于 NULL

计数

unsigned int num_users;
num_users = HASH_COUNT(users);
printf("there are %u users\n", num_users);

删除一个元素

void delete_user(Hash *del) {
    HASH_DEL(users, del);  /* user: pointer to deletee */
    free(del);             /* optional; it's up to you! */
}

说明

  1. users 是哈希表
  2. del 为指向待删除结构的指针

注意

  1. 删除一个元素并不会释放对应的内存,是否释放取决于用户,如上例中所示。
  2. 删除操作可能改变哈希表的指针(初始指向添加到哈希表中的第一个元素),比如删除了哈希表中的第一个元素

删除所有元素

void delete_all() {
    Hash *cur, *tmp;

    HASH_ITER(hh, users, cur, tmp) {
        HASH_DEL(users, cur);  /* delete; users advances to next */
        free(current_user);             /* optional- if you want to free  */
        cur = NULL;
    }
}

遍历

void print_users() {
    Hash *s;

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

说明

  • 遍历的顺序为元素的添加顺序
  • 也可以使用 hh.prev 从任意已知元素反向遍历

排序

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);
}

说明:第二个参数为排序函数,写法同 qsort 中用法。

key为字符串

键(key)为字符串型时,有以下两种情况:

  1. 结构体中是字符指针,例如 char* str; 使用 HASH_ADD_KEYPTR 操作
  2. 结构体中是字符数组,例如 char a[10]; 使用 HASH_ADD_STR 操作

字符数组型

/*
 * A string-keyed hash (string within structure) 
 */
#include <string.h>  /* strcpy */
#include <stdlib.h>  /* malloc */
#include <stdio.h>   /* printf */
#include "uthash.h"

struct my_struct {
    char name[10];             /* key (string is WITHIN the structure) */
    int id;
    UT_hash_handle hh;         /* makes this structure hashable */
};

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 != NULL) {
        printf("betty's id is %d\n", s->id);
    }

    /* free the hash table contents */
    HASH_ITER(hh, users, s, tmp) {
        HASH_DEL(users, s);
        free(s);
    }
    return 0;
}

字符指针型

/*
 * A string-keyed hash (structure points to string) 
 */
#include <string.h>  /* strcpy */
#include <stdlib.h>  /* malloc */
#include <stdio.h>   /* printf */
#include "uthash.h"

struct my_struct {
    const char *name;          /* key */
    int id;
    UT_hash_handle hh;         /* makes this structure hashable */
};

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 != NULL) {
        printf("betty's id is %d\n", s->id);
    }

    /* free the hash table contents */
    HASH_ITER(hh, users, s, tmp) {
        HASH_DEL(users, s);
        free(s);
    }
    return 0;
}

key为指针型

解释:这里表达的是指针自身作为 key。而不是指针指向的内容,后者使用 HASH_ADD_KEYPTR 处理。

/*
 * A pointer-keyed hash (pointer itself) 
 */
#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(int argc, char **argv)
{
    el_t *d;
    el_t *e = (el_t*)malloc(sizeof *e);
    if (e == NULL) {
        return -1;
    }
    e->key = (void*)someaddr;
    e->i = 1;
    HASH_ADD_PTR(hash, key, e);
    
    HASH_FIND_PTR(hash, &someaddr, d);
    if (d != NULL) {
        printf("found\n");
    }

  	/* release memory */
    HASH_DEL(hash, e);
    free(e);
    return 0;
}

key为结构体型

注意:

  • 为了满足字节对齐,结构体中通常包含无用对齐字节(padding),因此在添加和查找元素前必须先置零(must be zeroed,使用 memset ,然后再赋对应的值,执行添加或查找。
/*
 * A structure-keyed hash
 */
#include <stdlib.h>
#include <stdio.h>
#include "uthash.h"

typedef struct {
    char a;
    int b;
} record_key_t;

typedef struct {
    record_key_t key;
    /* ... other data ... */
    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);            /* important!! */
    r->key.a = 'a';
    r->key.b = 1;
    HASH_ADD(hh, records, key, sizeof(record_key_t), r);

    memset(&l, 0, sizeof(record_t));    /* important!! */
    l.key.a = 'a';
    l.key.b = 1;
    HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
    if (p != NULL) {
        printf("found %c %d\n", p->key.a, p->key.b);
    }

    HASH_ITER(hh, records, p, tmp) {
        HASH_DEL(records, p);
        free(p);
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值