uthash用法总结

2023-05-16

目录

  • 前言
  • 1、简单介绍
    • C语言中的HashTable
    • 支持的操作
    • 复杂度
    • 非库文件
  • 2、用法
    • 结构体定义
      • 键值
      • key的唯一性
      • UT_hash_handle
      • 内存消耗
    • 结构体声明
    • 添加
    • 查询
    • 删除
    • 清空哈希表
    • 统计hash表中的已经存在的元素数
    • 遍历
    • 排序
    • key为int类型的完整示例

前言

本文旨在总结介绍C开源hash项目。文章大部分内容均来自uthash的英文使用文档。官方源码
uthash实现了常见的hash操作函数。使用uthash的代码时只需要包含头文件“uthash.h”即可。该套代码所有的实现都在uthash.h文件中,因此只需要在自己的代码中包含头文件即可。

1、简单介绍

C语言中的HashTable

在C语言中不提供hash,这个软件提供一种数据结构,使得C支持哈希操作。

支持的操作

在哈希表中支持以下操作
add delete find count iterate sort

复杂度

add delete find都是O(1)的时间复杂度。

非库文件

NO,它仅是个头文件:uthash.h。你仅需要将这个头文件复制到你工程中即可。

#include "uthash.h"

2、用法

结构体定义

定义一个结构体

#include "uthash.h"

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

键值

对于key的数据类型没有限制

key的唯一性

在向哈希表中增加项目时,必须确保key值是唯一的。因此,在向哈希表中添加项目是,必须确保key没有被使用。可以通过使用HASH_FIND来确定key值是否已经在哈希表中存在。

UT_hash_handle

该句柄必须存在结构体中,它的作用是使得该哈希结构体可以工作。

内存消耗

一个哈希表项目在32位操作系统中占用32个字节。在64位操作系统中占用56个字节。可以使用HASH_OVERHEAD来获取哈希表的开销大小(以字节为单位)

结构体声明

struct my_struct *users = NULL;    /* important! initialize to NULL */

添加

key类型的不同,会导致调用的宏不同,在此我们以Key为int简单示例

void add_user(int user_id, char *name) {
    struct my_struct *s;
    HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? */
    if (s == NULL) {
      s = (struct my_struct *)malloc(sizeof *s);
      s->id = user_id;
      HASH_ADD_INT(users, id, s);  /* 这里必须明确告诉插入函数,自己定义的hash结构体中键变量的名字 */
    }
    strcpy(s->name, name);
}

查询

需要通过键值去查询哈希表。在下面代码中,users是哈希表,&user_id是键值。s是返回值。
如果键值存在,则会查询并返回对应的结构体指针,否则返回NULL。

struct my_struct *find_user(int user_id) {
    struct my_struct *s;

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

中间的变量是一个key的指针,不能直接传输一个字符串,而是需要将这个字符串赋值给一个变量,将变量的地址传输进去。

删除

从哈希表中删除一个结构体,必须已知一个指向该结构体的指针。(如果你仅有一个key,需要通过HASH_FIND去得到该结构体指针。

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

清空哈希表

//The HASH_ITER macro is a deletion-safe iteration construct which expands to a simple for loop.
void delete_all() {
  struct my_struct *current_user, *tmp;

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

统计hash表中的已经存在的元素数

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

如果users是NULL,这种情况count是0。

遍历

通过hh.next可以对整个哈希表从头至尾进行遍历。

void print_users() {
    struct my_struct *s;

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

同时也可以使用hh.prev从某个节点向前进行遍历。
由于 hh.prev 和 hh.next 字段,可以前后遍历散列中的项目。 因此散列也是一个双向链表。

排序

HASH_SORT(users, name_sort);

第二个参数是一个指向比较函数的指针。 它必须接受两个指针参数(要比较的项目),并且如果第一个项目分别在第二个项目之前、等于或之后排序,则必须返回一个小于零、零或大于零的 int。 (这与标准 C 库中 strcmp 或 qsort 使用的约定相同)。
下面,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);
}

当哈希中的项目被排序时,第一个项目可能会改变位置。 在上面的例子中,用户可能在调用 HASH_SORT 后指向不同的结构。

key为int类型的完整示例

包含"uthash.h"头文件后,代码可以直接在CodeBlock上直接运行。

#include <stdio.h>   /* gets */
#include <stdlib.h>  /* atoi, malloc */
#include <string.h>  /* strcpy */
#include "uthash.h"

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

struct my_struct *users = NULL;

void add_user(int user_id, char *name) {
    struct my_struct *s;

    HASH_FIND_INT(users, &user_id, s);  /* id already in the hash? */
    if (s == NULL) {
      s = (struct my_struct *)malloc(sizeof *s);
      s->id = user_id;
      HASH_ADD_INT(users, id, s);  /* id: name of key field */
    }
    strcpy(s->name, name);
}

struct my_struct *find_user(int user_id) {
    struct my_struct *s;

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

void delete_user(struct my_struct *user) {
    HASH_DEL(users, user);  /* user: pointer to deletee */
    free(user);
}

void delete_all() {
  struct my_struct *current_user, *tmp;

  HASH_ITER(hh, users, current_user, tmp) {
    HASH_DEL(users, current_user);  /* delete it (users advances to next) */
    free(current_user);             /* free it */
  }
}

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\n", 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\n");
        printf(" 2. add/rename user by id\n");
        printf(" 3. find user\n");
        printf(" 4. delete user\n");
        printf(" 5. delete all users\n");
        printf(" 6. sort items by name\n");
        printf(" 7. sort items by id\n");
        printf(" 8. print users\n");
        printf(" 9. count users\n");
        printf("10. quit\n");
        gets(in);
        switch(atoi(in)) {
            case 1:
                printf("name?\n");
                add_user(id++, gets(in));
                break;
            case 2:
                printf("id?\n");
                gets(in); id = atoi(in);
                printf("name?\n");
                add_user(id, gets(in));
                break;
            case 3:
                printf("id?\n");
                s = find_user(atoi(gets(in)));
                printf("user: %s\n", s ? s->name : "unknown");
                break;
            case 4:
                printf("id?\n");
                s = find_user(atoi(gets(in)));
                if (s) delete_user(s);
                else printf("id unknown\n");
                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\n", num_users);
                break;
            case 10:
                running = 0;
                break;
        }
    }

    delete_all();  /* free any structures */
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

uthash用法总结 的相关文章

  • js字符串包含另一个字符串_C语言中,在一个字符串中插入另一个字符串(字符)...

    本题可以看做一个字符串拼接问题 需要一个载体数组 includevoid insert array char s1 char s2 int n 思路 1 得到主串s1和子串s22 找到插入位置 3 进行插入 void main char s
  • 华为手机一键解锁工具箱下载 | 华为手机解BL锁软件: 支持解锁bootloader,刷写recovery功能

    文章目录 1 软件介绍2 特色功能3 资源站点4 下载地址5 软件截图6 安装教程7 使用教程7 1 解锁BL 1 软件介绍 通过这款华为手机实用工具箱可以对你的华为手机系列进行刷机 解锁等操作 xff0c 网上这种华为刷机解锁工具比较少
  • python subprocess 实时输出_Python标准库初探之subprocess

    一 subprocess简介 人生苦短 xff0c 我用Python 今天给大家带来一个在Python脚本中启动进程的利器 subprocess 人们都说Python是一个胶水语言 xff0c 可以方便地在多平台上调用其他指令 xff0c
  • 进程内存中堆和栈的区别

    1 概述 在整理数据结构时 xff0c 整理过栈 队列和堆 xff0c 但是在学习进程分布的时候又碰到了 栈和堆 xff0c 初学时很容易把这几个概念给弄混 xff0c 今天有空就给整理一下 2 程序在内存中的分布 程序在内存中的分布如下图
  • C++ Mutable

    1 mutable 含义及常规使用 mutable 英文中表示 xff0c 易变的 xff0c 不定的 xff1b 性情不定的 xff0c 而在代码中表示 可变数据成员 由前面整理的 const详解 知道 xff0c 由const修饰的成员
  • 牛吃草问题

    1 概述 最近碰到一个面试题 xff0c 讲的是牛吃草的问题 xff0c 当时时间短 xff0c 脑袋出现了短路 xff0c 没有给出答案 回来特意查了一下答案 xff0c 发现了一篇比较好的文章 xff0c 现在重新抄写一份 xff0c
  • 开始记录学习中的点滴

    随着年龄的增长 xff0c 除了去了很多地方之外 xff0c 感觉个人没有特别明显的成长 xff0c 对于未来充满了更多的迷茫与困惑 对于程序员的我来说更是感觉到了自己的瓶颈 xff0c 知识储备没有增加多少 xff0c 随着时间的流逝 x
  • C++中Struct与Class的区别与比较

    概述 之前只知道在C 43 43 中类和结构体的区别只有默认的防控属性 xff08 访问控制 xff09 不同 xff0c struct是public的 xff0c 而class是private的 但经过上网查资料才发现 xff0c 除了这
  • 函数调用约定的详解

    概述 在工作的过程中 xff0c 我们总是需要调用底层函数或者使用第三方的库 xff0c 在使用的过程中我就发现了有一些函数前面总有一些 stdcall xff0c 之初我只知道那是调用约定 xff0c 但别人问我什么是调用约定 xff0c
  • #pragma的常用方法讲解

    概述 我们在写代码时 xff0c 总会遇到头文件多次包含的情况 xff0c 刚开始时我们使用宏定义进行控制 xff0c 之后发现有 pragma once这样简单的东西 xff0c 当时是很兴奋 xff0c 以为 pragma就这一种用法
  • C++数组的详细解析

    概述 数组在写程序时经常用到 xff0c 但是对于它和指针的关系 xff0c 自己经常搞混 xff0c 所有抽点时间对数组进行整理 1 数组的概念和使用 数组是用来存储相同类型的变量的顺序集合 所有的数组都是由连续的内存位置组成 最低的地址
  • 华为荣耀9升降级系统 | 华为荣耀9变砖后如何救砖 | 华为荣耀9获取BL解锁码以及如何解BL锁 | 华为荣耀9如何通过写ramdisk.img来获取root

    文章目录 1 按2 通过官方华为手机助手升降级以及修复系统和安装驱动3 使用百分之五模式刷高维禁用包355来安装指定的系统版本8 0 0 3554 故意 xff08 或意外 xff09 刷错包把手机变砖5 使用救砖模式刷高维禁用包355来安
  • C++指针详解

    概述 C C 43 43 语言之所以强大 xff0c 以及其自由性 xff0c 很大部分体现在其灵活的指针运用上 因此 xff0c 说指针是C C 43 43 语言的灵魂一点都不为过 有好的一面 xff0c 必然会有坏的一面 xff0c 指
  • C++ lambda表达式及其原理

    概述 C 43 43 11中引入了新的lamdba表达式 xff0c 使用也很简单 xff0c 我最喜欢的是不用给函数取名称 xff0c 每次给函数取名称都感觉自己读书太少 1 lambda表达式 lambda表达式可以理解为一个匿名的内联
  • GIT 修改用户名和密码

    1 概述 如果你使用GIT的SSH 方式连接远端 xff0c 并且设置了一个没有口令的秘钥 xff0c 这样就可以砸不输入用户名和密码的情况下安全地传输数据 然而 xff0c 这对 HTTP 协议来说是不可能的 每一个连接都是需要用户名和密
  • bmi055 标定_Kalibr tutorials

    Kalibr installation tutorial I was confused about installing Kalibr but there is no even one hint in README md I just pu
  • python上位机例程_python 上位机通信实例

    34 moduleinfo 34 34 card count 34 34 count phone 34 1 34 count 34 1 34 search count 34 34 count phone 34 6 34 count 34 6
  • linux post请求_Linux C++网络编程

    img 前言 要想找一份Linux c 43 43 方面的好工作 xff0c 在面试过程中游刃有余 xff0c 那么这篇文章就是为你定制的 因为作为一个校招的学生 xff0c 我在学习和面试过程中的经历总这个体系的文章 xff0c 希望可以
  • 200826-C语言打印文件中的文本内容

    1 Description 在桌面上创建一个txt文件 xff0c 输入一些文本内容 xff0c 我们的任务是把文本内容打印出来 在编程之前 xff0c 关于一些函数的定义我们需要了解下 fopen fopen的函数原型为 xff1a FI
  • matlab设置使用vs2008编译器,64位操作系统下如何将matlab与vs2008的c编译器

    在windows sever 2008 操作系统上分别装了 matlab2009 xff0c vs2008 xff0c 想把 m 文件编译成 exe 文件 xff0c 但matlab找不到c的编译器 如下 xff0c 请教如何解决 gt g

随机推荐