glib使用之哈希表

来源:互联网 发布:今日头条采集 帝国cms 编辑:程序博客网 时间:2024/06/06 04:48

       我的glib学习之旅从哈希表开始。了解到了glib库这个很方便强大的库,正巧写程序的时候使用了一下,在这里做一次总结,基于glib-2.50.3源码。
       Glib提供了很多方便的功能,这一次主要用到glib提供的数据结构,哈希表,相关接口说明文档这里。
       哈希表所描述的是一种键值对的对应关系,通过给定的key,能够快速的找到对应的value。也就是说哈希表表达的是key到value的映射关系。这一次着重说明一下如何使用glib为管理哈希表提供的接口。
       通常对于一个哈希表的操作包括,创建哈希表,删除哈希表以及对于哈希表每一个条目的增删改查等。这里面最大的一个陷阱在于内存的回收方面,下面也有重点提及。毕竟对于在哈希表的实现方面,管理内存是很重要的。
创建一个哈希表:
       由于C语言没有函数重载的功能,可以看到glib提供如下两个创建哈希表的接口函数:

GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func);GHashTable* g_hash_table_new_full(GHashFunc hash_func,GEqualFunc key_equal_func,GDestroyNotify key_destroy_func,GDestroyNotify value_destroy_func);

       g_hash_table_new_full函数中四个参均是函数指针,其的意义如下:hash_func函数是将key值转换成为key_hash,这里面key_hash通常是一个无符号的整形,而value值的索引则是将key_hash模除哈希表的大小;由于key的值则可能是数字,字符串,结构体,因此我通常的做法是把key这段内存空间按照8字节拼接起来,形成一个字符串,然后由g_str_hash生成对应的哈希值。key_equal_func函数是比较key值的函数,在插入删除等操作的时候都会用到该函数。因为key值可能是整数,字符串或者结构体,因此比较不同类型数据是否相同的的方法在该函数内实现。key_destroy_func是key值内存的释放函数。value_destroy_func是value值内存的释放函数。key_destroy_func以及value_destroy_func是容易出现错误的地方,在删除哈希表条目的时候再提。g_hash_table_new 函数就是默认的key_destroy_func 和value_destroy_func的值为NULL。
删除一个哈希表:
       函数接口是void g_hash_table_destroy (GHashTable *hash_table)。C语言常见的问题之一就是内存泄露问题。这时候上述所提到的key_destroy_func函数以及value_destroy_func函数的作用就出来了,主要是针对key和value所指向的内存是动态申请的。这两个函数的作用就是定义如何释放key和value对应的内存空间,在调用g_hash_table_destroy函数的时候会触发key_destroy_func以及value_destroy_func函数。
增加哈希表条目:

gbooleang_hash_table_insert (GHashTable *hash_table,gpointer key,gpointer value);

       我一开始想当然的以为如果系统中存在,就会不插入该条目,然后就没有其他操作了,实际上,这里面就是一个坑,源码中还有其他的操作。向hash_table表中插入key和value。如果原来的哈希表中不存在相同的key,那么直接插入没有问题;如果原来哈希表中存在一样的key的话,则触发key_destroy_func函数释放传递进来参数key所代表的空间,同时释放哈希表中老value空间,并将传递进来新的value加入到哈希表中,这样可以保证哈希表中存在的是最新的value值。这和后面的g_hash_table_replace修改哈希条目是有所不同的。这个时候如果key是常量字符串等存储在全局数据区的话,释放时候就会发生错误;另外如果key值是value值的一部分的话,也可能会出错。
删除哈希表条目:

gboolean g_hash_table_remove (GHashTable *hash_table,gconstpointer key);

       删除hash_table表中key值所表示的一条记录,这时候同样会触发key_destroy_func函数以及value_destroy_func函数。其实删除整个哈希表也是一步步释放哈希表的每一个条目的。
修改哈希表条目:

gbooleang_hash_table_replace (GHashTable *hash_table,gpointer key,gpointer value);

       该函数提供修改key值所对应value值的操作,如果哈希表中不存在该key值,则该函数跟g_hash_table_insert的功能一致;如果哈希表中存在相同的key值,和g_hash_table_insert的区别在于,触发key_destroy_func函数释放哈希表中老key所代表的空间,并将传递进来新的key加入到哈希表中;同时触发value_destroy_func函数释放哈希表中老的value空间,并将传递进来新的value加入到哈希表中。
查找哈希表条目:

gpointer g_hash_table_lookup (GHashTable *hash_table,gconstpointer key)

       查找相同的key,返回对应的value指针地址,找不到则为NULL。
遍历哈希表:

g_hash_table_iter_init (&iter, hash_table);while (g_hash_table_iter_next (&iter, &key, &value))  {    // do something with key and value  }

       这个接口文档给出的事例,如果了解遍历器,这里应该很好理解。
       综上,在使用哈希表的时候,关于key_destroy_func 和value_destroy_func函数的编写以及使用要注意两点:1、传参的时候,不要传入在全局数据区的变量,因为这两个释放函数是针对动态申请内存的。2、key最好和value相互独立,从上面的分析可以看出,如果key是value的一部分,则很有可能会出现错误。
       另外glib的哈希表解决地址冲突的方法不像我们当时课本上面以及通常大家使用的链表方式,而是基于数组的。数组的优势就是一块连续的内存空间,因此在按照现有的计算机缓存结构来说,访问效率更高。因此下一步是研究一下glib是如何设计实现哈希表的。
       本文为CSDN村中少年原创文章,转载记得加上小尾巴偶,博主链接这里。

0 0