• 1. C语言中的哈希
  • 2. 它能做什么?
  • 3. 您的结构
    • 1. The key
    • 2. Unique keys(唯一键值)
    • 3. 哈希句柄
  • 2. 关于memory
    • 1. 开销
    • 2. 清理如何进行
  • 3. 哈希操作
    • 1.宏引用
    • 2. 声明哈希
    • 3. 添加项目
    • 检查唯一性
    • 4. 替换项目
    • 5. 查找项目
    • 6. 删除项目
      • 1. 单个删除
      • 2. 迭代删除
      • 3. 一次性全部删除
    • 7. 对项目进行计数
    • 8. 迭代和排序
      • 1. 迭代
      • 2. 排序
  • 4. 一个完整的例子
  • 5. Standard key 类型
    • 1. 整数键值
    • 2. 字符串键值
    • 3. 指针键值
    • 4. 结构体键值
  • 6. 参数说明

1. C语言中的哈希

在 C 中,哈希没有存在于语言本身中。因此使用起来很麻烦,该软件为C提供了一个哈希表结构。项目开源在github上,uthash下载地址,下载之后是.h头文件,功能都是宏实现的,因此可以直接在你的项目中添加#include"uthash.h"之后就能使用,本文章是对于官网的翻译,方便大家好理解。官方文档地址

2. 它能做什么?

此软件支持对哈希表中的项目进行以下操作:

1.添加/替换

2.找到

3.删除

4.计数

5.迭代

6.排序

3. 您的结构

在 uthash 中,哈希表由结构组成。每个结构代表一个 键值关联。一个或多个结构字段构成键。 结构指针本身就是值。

定义可散列的结构

#include "uthash.h"struct my_struct {int id;                    /* key */char name[10];UT_hash_handle hh;         /* makes this structure hashable */
};

请注意,在 uthash 中,当您将结构添加到哈希表中时,您的结构永远不会被移动或复制到另一个位置。这意味着您可以保留安全指向您的结构的其他数据结构 - 无论您在程序的生存期内是在哈希表中添加还是删除它。

1. The key

对键字段的数据类型或名称没有限制。键还可以包含多个具有任何名称和数据类型的连续字段。

任何数据类型…真?
是的,您的键和结构可以具有任何数据类型。与具有固定原型的函数调用不同,uthash 由宏组成——其参数是非类型的——因此能够使用任何类型的结构或键。

2. Unique keys(唯一键值)

与任何哈希一样,每个项目都必须有一个唯一的键。应用程序必须强制实施密钥唯一性。在将项目添加到哈希表之前,您必须首先知道(如果有疑问,请检查!)该密钥尚未使用。您可以使用 HASH_FIND 检查哈希表中是否已存在键。

3. 哈希句柄

结构中必须存在UT_hash_handle字段。它用于使哈希工作的内部簿记。它不需要初始化。它可以命名为任何东西,但您可以通过命名 hh 来简化事情。这允许您使用更简单的“方便”宏来添加、查找和删除项目。

2. 关于memory

1. 开销

在32位系统中,散列句柄每项消耗大约32个字节,在64位系统中每项消耗大约56个字节。其他间接费用 - 存储桶和 表–相比之下可以忽略不计。

2. 清理如何进行

有人问uthash如何清理其内部存储器。答案很简单:当您从哈希表中删除最后一项时,uthash 会释放所有 与该哈希表关联的内部存储器,并将其指针设置为 NULL。

3. 哈希操作

1.宏引用

uthash 宏分为两类,便利宏与常规宏。

便利宏:
便利宏与常规宏执行相同的操作,但需要较少的参数。为了使用方便的宏,结构的UT_hash_handle字段必须命名为 hh,并且对于“添加”或“查找”,键字段的类型必须是 int 或 char[] 或指针。对应HASH_ADD_INT,HASH_ADD_STR,HASH_ADD_PTR。

常规宏:

文章暂时只介绍便利宏,常规宏暂时不做说明。

2. 声明哈希

使用前您的哈希必须声明为指向结构的 -initialized 指针。NULL

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

3. 添加项目

根据需要分配和初始化结构。对 uthash 来说,唯一重要的方面是您的密钥必须初始化为唯一值。然后调用 .(这里我们使用便利宏,它为类型的键提供了简化的用法)。HASH_ADDHASH_ADD_INT(增加int类型key值)

void add_user(int user_id, char *name) {struct my_struct *s;s = malloc(sizeof *s);s->id = user_id;strcpy(s->name, name);HASH_ADD_INT(users, id, s);  /* id: name of key field */
}

第一个参数是哈希表,第二个参数是键字段的名称。最后一个参数是指向正在添加的结构的指针。
密钥在使用过程中不得修改
将结构添加到哈希后,请勿更改其键的值。相反,请从哈希中删除该项,更改密钥,然后重新添加它。

检查唯一性

在上面的示例中,我们没有检查是否已经是哈希中某些现有项目的键。如果程序有可能生成重复的密钥,则必须在将密钥添加到哈希之前显式检查唯一性。如果键已在哈希中,则只需修改哈希中的现有结构,而无需添加项。将具有相同键的两个项目添加到哈希表是错误的。

让我们重写函数以检查 id 是否在哈希中。只有当 id 不存在哈希时,我们才会创建项目并添加它。否则,我们只是修改已经存在的结构。

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

4. 替换项目

HASH_REPLACE_INT宏等效于HASH_ADD_INT宏,只是它首先尝试查找和删除项目。如果它找到并删除了某个项目,它还将返回该项目指针作为输出参数。

5. 查找项目

要在哈希中查找结构,您需要它的键。然后调用 HASH_FIND_INT

struct my_struct *find_user(int user_id) {struct my_struct *s;HASH_FIND_INT(users, &user_id, s);  /* s: output pointer */return s;
}

找到相应键值时,s返回给定键的结构,当找不到时s返回NULL。
中间的参数是一个指向键的指针。你不能把一个字面的键值传递,而是把字面的值分配给一个变量,并传递一个指向该变量的指针。

6. 删除项目

1. 单个删除

要从哈希中删除一个结构,你必须有一个指向它的指针。(如果你只有键,首先要做的是获得结构的指针)。

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

删除一个结构只是把它从哈希表中删除–它并没有删除。何时释放你的结构,完全由你自己决定;uthash永远不会释放你的结构。例如,当使用宏时,被替换的输出参数会被返回,以使用户有可能去分配它。

hash表指针(最初指向添加到散列中的第一个项目)可以响应而改变(即如果你删除了散列表中的第一个项目)。

2. 迭代删除

该宏是一个删除安全的迭代结构,可扩展为一个简单的for循环。 HASH_ITER

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  */}
}

3. 一次性全部删除

如果你只想删除所有的项目,但不释放它们或做任何按元素的清理,你可以在一个操作中更有效地完成。

HASH_CLEAR(hh, users);

之后,列表头将被设置为NULL

7. 对项目进行计数

哈希表中的项目数可以使用以下方法获得:HASH_COUNT

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

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

8. 迭代和排序

1. 迭代

你可以通过从头开始并跟随指针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

由于字段的存在,在哈希中向前和向后迭代项目是可能的。哈希中的所有项目都可以通过反复遵循这些指针来到达,因此哈希也是一个 双向链表。

2. 排序

HASH_SORT(users, name_sort);

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

int sort_function(void *a, void *b) {/* compare a to b (cast a and b appropriately)* return (int) -1 if (a < b)* return (int)  0 if (a == b)* return (int)  1 if (a > b)*/
}

下面是排序函数的两个示例。name_sort id_sort

int by_name(const struct my_struct *a, const struct my_struct *b) {return strcmp(a->name, b->name);
}int by_id(const struct my_struct *a, const struct my_struct *b) {return (a->id - b->id);
}void sort_by_name() {HASH_SORT(users, by_name);
}void sort_by_id() {HASH_SORT(users, by_id);
}

4. 一个完整的例子

#include <stdio.h>   /* printf */
#include <stdlib.h>  /* atoi, malloc */
#include <string.h>  /* strcpy */
#include "uthash.h"struct my_struct {int id;                    /* key */char name[21];UT_hash_handle hh;         /* makes this structure hashable */
};struct my_struct *users = NULL;void add_user(int user_id, const 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 is the 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;struct my_struct *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 by_name(const struct my_struct *a, const struct my_struct *b)
{return strcmp(a->name, b->name);
}int by_id(const struct my_struct *a, const struct my_struct *b)
{return (a->id - b->id);
}const char *getl(const char *prompt)
{static char buf[21];char *p;printf("%s? ", prompt); fflush(stdout);p = fgets(buf, sizeof(buf), stdin);if (p == NULL || (p = strchr(buf, '\n')) == NULL) {puts("Invalid input!");exit(EXIT_FAILURE);}*p = '\0';return buf;
}int main()
{int id = 1;int running = 1;struct my_struct *s;int temp;while (running) {printf(" 1. add user\n");printf(" 2. add or 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");switch (atoi(getl("Command"))) {case 1:add_user(id++, getl("Name (20 char max)"));break;case 2:temp = atoi(getl("ID"));add_user(temp, getl("Name (20 char max)"));break;case 3:s = find_user(atoi(getl("ID to find")));printf("user: %s\n", s ? s->name : "unknown");break;case 4:s = find_user(atoi(getl("ID to delete")));if (s) {delete_user(s);} else {printf("id unknown\n");}break;case 5:delete_all();break;case 6:HASH_SORT(users, by_name);break;case 7:HASH_SORT(users, by_id);break;case 8:print_users();break;case 9:temp = HASH_COUNT(users);printf("there are %d users\n", temp);break;case 10:running = 0;break;}}delete_all();  /* free any structures */return 0;
}

5. Standard key 类型

1. 整数键值

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

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];             /* 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) 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;
}

字符指针:

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

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\n");/* release memory */HASH_DEL(hash, e);free(e);return 0;
}

4. 结构体键值

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

#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);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\n", p->key.a, p->key.b);HASH_ITER(hh, records, p, tmp) {HASH_DEL(records, p);free(p);}return 0;
}

6. 参数说明

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

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

keyfield_name
  结构中键字段的名称。(对于多字段键,这是键的第一个字段)。如果您不熟悉宏,则将字段名称作为参数传递似乎很奇怪。请参阅 注释。

key_len
  键字段的长度(以字节为单位)。例如,对于整数键,它是 sizeof(int),而对于字符串键,它是strlen(key)。(有关多字段键,请参阅此注释。)

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

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

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语言uthash使用指南相关推荐

  1. 来自 Google 的 R 语言编码风格指南

    来自 Google 的 R 语言编码风格指南 R 语言是一门主要用于统计计算和绘图的高级编程语言. 这份 R 语言编码风格指南旨在让我们的 R 代码更容易阅读.分享和检查. 以下规则系与 Google ...

  2. 《C语言编程初学者指南》一2.9 理解运算符优先级

    本节书摘来自异步社区<C语言编程初学者指南>一书中的第2章,第2.9节,作者[美]Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云 ...

  3. 《C语言编程初学者指南》一1.5 使用程序语句

    本节书摘来自异步社区<C语言编程初学者指南>一书中的第1章,第1.5节,作者[美]Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云 ...

  4. css 样式三元运算_CSS扩展语言——Sass入门指南

    一.Sass概况 今天来聊聊sass吧,之前用了很久的less,刚开始接触的时候感觉这东西就是个神器. 写CSS时间长了自然就能发现CSS在书写的时候的不足之处,不能嵌套,没有变量,更加不能像js那样 ...

  5. 《C语言编程初学者指南》一1.9 本章小结

    本节书摘来自异步社区<C语言编程初学者指南>一书中的第1章,第1.9节,作者[美]Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云 ...

  6. 《C语言编程初学者指南》一导读

    前言 C语言编程初学者指南 C语言是一种强大的基于过程的编程语言,它于1972年由Dennis Ritchie在贝尔实验室发明.C语言最初是开发来用于UNIX平台的,但却已经扩展到很多其他的系统和应用 ...

  7. C语言SOCKET编程指南

    转载自:http://blog.sina.com.cn/s/blog_79b01f66010163q3.html 这篇文章完全可以作为c语言socket编程指南,无论在任何系统下.感谢作者fenglo ...

  8. 【C语言避坑指南】学习记录

    学习自B站Up双笙子佯谬的视频 [C语言避坑指南]看完这个视频,我再也不害怕指针!_哔哩哔哩_bilibili课件:https://github.com/parallel101/course作业:ht ...

  9. C语言入坑指南-缓冲区溢出

    前言 缓冲区溢出通常指的是向缓冲区写入了超过缓冲区所能保存的最大数据量的数据.如果说之前所提到的一些问题可能只是影响部分功能的实现,那么缓冲区溢出将可能会造成程序运行终止,被不安全代码攻击等严重问题, ...

最新文章

  1. Generate a String CodeForces - 710E(dp)
  2. Qt中的Q_OBJECT
  3. 【折腾】斐讯N1 安装 Docker + GUI
  4. 百度对TOP等冷门域名冷淡
  5. java调用Linux mahout,Mahout算法调用展示平台2.1
  6. 日更第8期-2015-3-23-如何科学地使用因特网-第三讲-为什么要用Git Bash?咱们用Github for Windows吧!(上)...
  7. DBCC CHECKDB
  8. 饭卡管理系统mysql_数据库饭卡管理系统.doc
  9. 手机无线电驾驶与马歇尔·麦克卢汉的哲学
  10. windows2003视频教程
  11. linux ext4 文件大小,刨根问底:ext3/ext4文件系统最大空间及单个文件大小演算法则...
  12. 【单位换算】存储单位(bit Byte KB MB GB TB PB EB ZB YB BB)时间单位(ms μs ns ps)长度单位(dm cm mm μm nm pm fm am zm ym)
  13. Java基础教程:dubbo源码解析-服务暴露与发现
  14. windows调节屏幕文字清晰度、锐度,屏幕字体模糊怎么办,屏幕字体不清晰
  15. 更新fielddata为true_[翻译]Elasticsearch重要文章之五:预加载fielddata
  16. 8K播放网络全终端播放器H5播放器网页直播/点播播放器EasyPlayer和vlc播放RTSP流地址不兼容问题排查解决
  17. 生鲜配送管理系统_升鲜宝V2.0 供应商协同系统设计思想及设计效果展现(一)...
  18. 考研专业课c语言与数据结构,南开大学816 C语言与数据结构2018考研专业课大纲...
  19. win10下基于anaconda利用keras开展16系显卡GTX1650的GPU神经网络计算
  20. 哈工大软件构造lab2实验报告

热门文章

  1. java调sqlloader,Java调用SqlLoader将大文本数据导入数据库
  2. python怎么画折线图
  3. windows免费版切割pdf拆分pdf提取pdf指定页码小工具
  4. 如何获取显示器的刷新频率?
  5. error C2664: “strcmp”: 不能将参数 1 从“WCHAR [260]”转换为“const char *”
  6. 虚拟机Oracle BIEE下载
  7. echarts 全国地图,省市地图案例案例代码
  8. js 等待几秒 每隔几秒 执行
  9. 用选择排序法对键盘输入的n个数字升序排序,要求用函数来实现,n的数值也由键盘输入数据来确定。
  10. 支持批量转换的全能pdf转换器