简介
Hash是一种常见的数据结构,GLib提供了操作Hash表的多种操作函数。
数据结构
Hash表有两个重要的数据结构,GHashTable和GHashTableIter,这两者都是不透明的数据结构,可以整体使用它,但无法单独使用其中的一个结构体成员。GHashTable是Hash表本身相关,GHashTableIter与迭代器相关,用来遍历或操作Hash表中的数据。
Hash表:
typedef struct _GHashTable GHashTable;
迭代器:
typedef struct _GHashTableIter GHashTableIter;
函数列表
GHashTable * g_hash_table_new ()
GHashTable * g_hash_table_new_full ()
guint (*GHashFunc) ()
gboolean (*GEqualFunc) ()
gboolean g_hash_table_insert ()
gboolean g_hash_table_replace ()
gboolean g_hash_table_add ()
gboolean g_hash_table_contains ()
guint g_hash_table_size ()
gpointer g_hash_table_lookup ()
gboolean g_hash_table_lookup_extended ()
void g_hash_table_foreach ()
gpointer g_hash_table_find ()
void (*GHFunc) ()
gboolean g_hash_table_remove ()
gboolean g_hash_table_steal ()
guint g_hash_table_foreach_remove ()
guint g_hash_table_foreach_steal ()
void g_hash_table_remove_all ()
void g_hash_table_steal_all ()
GList * g_hash_table_get_keys ()
GList * g_hash_table_get_values ()
gpointer * g_hash_table_get_keys_as_array ()
gboolean (*GHRFunc) ()
#define g_hash_table_freeze()
#define g_hash_table_thaw()
void g_hash_table_destroy ()
GHashTable * g_hash_table_ref ()
void g_hash_table_unref ()
void g_hash_table_iter_init ()
gboolean g_hash_table_iter_next ()
GHashTable * g_hash_table_iter_get_hash_table ()
void g_hash_table_iter_replace ()
void g_hash_table_iter_remove ()
void g_hash_table_iter_steal ()
gboolean g_direct_equal ()
guint g_direct_hash ()
gboolean g_int_equal ()
guint g_int_hash ()
gboolean g_int64_equal ()
guint g_int64_hash ()
gboolean g_double_equal ()
guint g_double_hash ()
gboolean g_str_equal ()
guint g_str_hash ()
函数功能分类
创建
GHashTable * g_hash_table_new ()
GHashTable * g_hash_table_new_full ()
销毁
void g_hash_table_destroy ()
插入
gboolean g_hash_table_insert ()
gboolean g_hash_table_add ()
替换
gboolean g_hash_table_replace ()
删除
gboolean g_hash_table_remove ()
gboolean g_hash_table_steal ()
guint g_hash_table_foreach_remove ()
guint g_hash_table_foreach_steal ()
void g_hash_table_remove_all ()
void g_hash_table_steal_all ()
查找
gboolean g_hash_table_contains ()
gpointer g_hash_table_lookup ()
gboolean g_hash_table_lookup_extended ()
gpointer g_hash_table_find ()
GList * g_hash_table_get_keys ()
GList * g_hash_table_get_values ()
gpointer * g_hash_table_get_keys_as_array ()
遍历
void g_hash_table_foreach ()
测长
guint g_hash_table_size ()
引用解引用
GHashTable * g_hash_table_ref ()
void g_hash_table_unref ()
迭代器
void g_hash_table_iter_init ()
gboolean g_hash_table_iter_next ()
GHashTable * g_hash_table_iter_get_hash_table ()
void g_hash_table_iter_replace ()
void g_hash_table_iter_remove ()
void g_hash_table_iter_steal ()
hash函数
gboolean g_direct_equal ()
guint g_direct_hash ()
gboolean g_int_equal ()
guint g_int_hash ()
gboolean g_int64_equal ()
guint g_int64_hash ()
gboolean g_double_equal ()
guint g_double_hash ()
gboolean g_str_equal ()
guint g_str_hash ()
函数功能说明及综合演示
基本功能演示
创建
GHashTable * g_hash_table_new ()
GHashTable * g_hash_table_new_full ()
在创建hash表时传入每个表节点的key及value的释放函数,当hash表被销毁时,对每个节点执行这两个释放函数。
如果有释放函数时,调用remove接口删除一个节点会调到释放函数,这一点后面讲删除一节时会详细讲到。
测长
guint g_hash_table_size ()
销毁
void g_hash_table_destroy ()
插入
gboolean g_hash_table_insert ()
gboolean g_hash_table_add ()
当要插入的key和value相同时,可以使用本函数,不需要再单独传入value
遍历
void g_hash_table_foreach ()
下面演示在不同key和value数据类型情况以上函数的使用方法。
演示程序1:key为int,value为int,key和value值相等
源码见glib_examples\glib_hash\glib_hash_basic_key_int_value_int
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_int_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s: key:%d, value:%d \n", (gchar *)user_data, *(gint *)key, *(gint *)value);
return;
}
static void test_hash_basic(void)
{
gint i = 0;
gint arr[TEST_ARRAY_LEN] = {0};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new(g_int_hash, g_int_equal);
for(i=0; i<TEST_ARRAY_LEN/2; i++) {
arr[i] = i;
g_hash_table_insert(htable, &arr[i], &arr[i]);
}
for(i=TEST_ARRAY_LEN/2;i<TEST_ARRAY_LEN;i++) {
arr[i] = i;
g_hash_table_add(htable, &arr[i]);
}
g_hash_table_foreach(htable, _test_hash_int_foreach_func, "int");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
int: key:0, value:0
int: key:1, value:1
int: key:3, value:3
int: key:2, value:2
int: key:6, value:6
int: key:7, value:7
int: key:5, value:5
int: key:8, value:8
int: key:4, value:4
int: key:9, value:9
htable length: 10
OK
演示程序2:key为字符串,value也为字符串,key和value值相等
源码见glib_examples\glib_hash\glib_hash_basic_key_str_value_str
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s: key:%s, value:%s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void test_hash_basic(void)
{
gint i = 0;
char *arr[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new(g_str_hash, g_str_equal);
for(i=0; i<TEST_ARRAY_LEN/2; i++) {
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_insert(htable, arr[i], arr[i]);
}
for(i=TEST_ARRAY_LEN/2;i<TEST_ARRAY_LEN;i++) {
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_add(htable, arr[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "str");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
for(i=0; i<TEST_ARRAY_LEN; i++) {
g_free(arr[i]);
}
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
str: key:str9, value:str9
str: key:str0, value:str0
str: key:str1, value:str1
str: key:str2, value:str2
str: key:str3, value:str3
str: key:str4, value:str4
str: key:str5, value:str5
str: key:str6, value:str6
str: key:str7, value:str7
str: key:str8, value:str8
htable length: 10
OK
演示程序3:key为int,value为字符串
源码见glib_examples\glib_hash\glib_hash_basic_key_int_value_str
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_mix_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s: key:%d, value:%s \n", (gchar *)user_data, *(gint *)key, (gchar *)value);
return;
}
static void _test_hash_mix_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_basic(void)
{
gint i = 0;
gint iarr[TEST_ARRAY_LEN] = {0};
gchar *arr[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, _test_hash_mix_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
iarr[i] = i;
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_insert(htable, &iarr[i], arr[i]);
}
g_hash_table_foreach(htable, _test_hash_mix_foreach_func, "mix");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
mix: key:0, value:str0
mix: key:1, value:str1
mix: key:3, value:str3
mix: key:2, value:str2
mix: key:6, value:str6
mix: key:7, value:str7
mix: key:5, value:str5
mix: key:8, value:str8
mix: key:4, value:str4
mix: key:9, value:str9
htable length: 10
OK
演示程序4:key为int,value为字符串(错误示例,有段错误)
源码见glib_examples\glib_hash\glib_hash_basic_key_int_value_str_err
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_mix_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s: key:%d, value:%s \n", (gchar *)user_data, GPOINTER_TO_INT(key), (gchar *)value);
return;
}
static void _test_hash_mix_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_basic(void)
{
gint i = 0;
char *arr[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, _test_hash_mix_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_insert(htable, GINT_TO_POINTER(i), arr[i]);
}
g_hash_table_foreach(htable, _test_hash_mix_foreach_func, "mix");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
段错误(吐核)
运行时有段错误,需要进行以下修改。
将上述hash表创建函数传入g_direct_hash和g_direct_equal
即,将
htable = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, _test_hash_mix_value_destroy_func);
改为:
htable = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, _test_hash_mix_value_destroy_func);
再次运行,不会出现段错误。
在创建时指定节点释放函数
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_new_full
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s: key:%s, value:%s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_basic(void)
{
gint i = 0;
char *arr[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, _test_hash_str_key_destroy_func, NULL);
for(i=0; i<TEST_ARRAY_LEN/2; i++) {
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_insert(htable, arr[i], arr[i]);
}
for(i=TEST_ARRAY_LEN/2;i<TEST_ARRAY_LEN;i++) {
arr[i] = g_strdup_printf("str%d", i);
g_hash_table_add(htable, arr[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "str");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
str: key:str9, value:str9
str: key:str0, value:str0
str: key:str1, value:str1
str: key:str2, value:str2
str: key:str3, value:str3
str: key:str4, value:str4
str: key:str5, value:str5
str: key:str6, value:str6
str: key:str7, value:str7
str: key:str8, value:str8
htable length: 10
OK
替换
gboolean g_hash_table_replace ()
替换hash表中的key和value。如果key已经存在,则将原key和value替换掉。如果在创建hash表时指定了key_destroy_func,则替换前本函数会被调用到,如果指定了value_destroy_func,则替换前本函数会被调用到。如果key不存在,则本函数功能相当于g_hash_table_insert。
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_replace
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_print("free key: %s \n", (gchar *)data);
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_print("free value: %s \n", (gchar *)data);
g_free(data);
}
}
static void test_hash_basic(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN-2; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
//key already exist
key[TEST_ARRAY_LEN-2] = g_strdup_printf("key%d", 1);
val[TEST_ARRAY_LEN-2] = g_strdup_printf("value%d", 999);
g_hash_table_replace(htable, key[TEST_ARRAY_LEN-2], val[TEST_ARRAY_LEN-2]);
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "replace-key-exist");
g_print("htable length: %d \n", g_hash_table_size(htable));
//key not exist
key[TEST_ARRAY_LEN-1] = g_strdup_printf("key%d", 888);
val[TEST_ARRAY_LEN-1] = g_strdup_printf("value%d", 8888);
g_hash_table_replace(htable, key[TEST_ARRAY_LEN-1], val[TEST_ARRAY_LEN-1]);
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "replace-key-not-exist");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
ori key: key0, value: value0
ori key: key1, value: value1
htable length: 2
free key: key1
free value: value1
replace-key-exist key: key0, value: value0
replace-key-exist key: key1, value: value999
htable length: 2
replace-key-not-exist key: key888, value: value8888
replace-key-not-exist key: key0, value: value0
replace-key-not-exist key: key1, value: value999
htable length: 3
free key: key888
free value: value8888
free key: key0
free value: value0
free key: key1
free value: value999
OK
查找
// 查找key是否在hash表中
gboolean g_hash_table_contains ()
// 查找key是否在hash表中,如果存在,则返回value的地址。如果不存在,则返回NULL。
// 本函数无法区分key不存在还是key存在但value为空的情况,如果想区分这两者,可使用g_hash_table_lookup_extended函数。
gpointer g_hash_table_lookup ()
// 查找key是否存在hash表中,如果存在,则返回key和value的地址。
// 如果想要删除一个节点,且未设置key和value的释放函数,可以先调用本函数查找到该节点,得到key和value的指针地址,然后自行释放。
gboolean g_hash_table_lookup_extended ()
// 自定义查找函数在hash表中查找key或value是否存在,若存在,则返回节点的value。注意本函数查找效率不如g_hash_table_lookup_extended高。
gpointer g_hash_table_find ()
// 返回hash表的所有key组成的链表,注意链表的每个节点只是保存了key的指针,内存仍然属于hash表。返回的GList需要调用g_list_free释放(这里释放的是节点,每个节点所指向的值,并没有被释放)。
GList * g_hash_table_get_keys ()
// 与g_hash_table_get_keys类似,这里返回的是所有value组成的链表。
GList * g_hash_table_get_values ()
// 返回hash表中所有key,但返回的是数组形式。与g_hash_table_get_keys类似,数组只保存了key的指针,内存仍属于hash表。
gpointer * g_hash_table_get_keys_as_array ()
查找系列函数示例代码如下:
源码见glib_examples\glib_hash\glib_hash_find
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_contains(void)
{
gint i = 0;
gboolean ret = FALSE;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
ret = g_hash_table_contains(htable, key[TEST_ARRAY_LEN/2]);
g_print("%s %s in hash table \n", key[TEST_ARRAY_LEN/2], ret?"is":"is Not");
g_hash_table_destroy(htable);
return;
}
static void test_hash_lookup(void)
{
gint i = 0;
char *value = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
value = g_hash_table_lookup(htable, key[1]);
if(NULL == value) {
g_print("key[1] value is NULL \n");
} else {
g_print("key[1] value is %s \n", value);
}
value = g_hash_table_lookup(htable, key[2]);
if(NULL == value) {
g_print("key[1] value is NULL \n");
} else {
g_print("key[2] value is %s \n", value);
}
value = g_hash_table_lookup(htable, "key9");
if(NULL == value) {
g_print("key9 value is NULL \n");
} else {
g_print("key9 value is %s \n", value);
}
g_hash_table_destroy(htable);
return;
}
static void test_hash_lookup_extended(void)
{
gint i = 0;
gboolean ret = FALSE;
char *ori_key = NULL;
char *ori_value = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
ret = g_hash_table_lookup_extended(htable, "key1", (gpointer *)&ori_key, (gpointer *)&ori_value);
if(TRUE == ret) {
g_print("key1 found, ori_key: %s, value: %s \n", ori_key, ori_value);
} else {
g_print("key1 not found \n");
}
ret = g_hash_table_lookup_extended(htable, "key9", (gpointer *)&ori_key, (gpointer *)&ori_value);
if(TRUE == ret) {
g_print("key9 found, ori_key: %s, value: %s \n", ori_key, ori_value);
} else {
g_print("key9 not found \n");
}
g_hash_table_destroy(htable);
return;
}
static gboolean _test_hash_find_ghrfunc
(gpointer key, gpointer value, gpointer user_data)
{
if(0 == g_strcmp0((const char *)user_data, (const char *)key)) {
return TRUE;
}
return FALSE;
}
static void test_hash_find(void)
{
gint i = 0;
char *value = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
value = g_hash_table_find(htable, _test_hash_find_ghrfunc, "key2");
if(NULL != value) {
g_print("key2 found in hash table, value: %s \n", value);
} else {
g_print("key2 not found in hash table \n");
}
value = g_hash_table_find(htable, _test_hash_find_ghrfunc, "key9");
if(NULL != value) {
g_print("key9 found in hash table, value: %s \n", value);
} else {
g_print("key9 not found in hash table \n");
}
g_hash_table_destroy(htable);
return;
}
static void _test_hash_list_foreach_func(gpointer data, gpointer user_data)
{
g_print("list data: %s \n", (gchar *)data);
}
static void test_hash_get_keys(void)
{
gint i = 0;
GList *list = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
list = g_hash_table_get_keys(htable);
g_list_foreach(list, _test_hash_list_foreach_func, NULL);
g_list_free(list);
g_hash_table_destroy(htable);
return;
}
static void test_hash_get_values(void)
{
gint i = 0;
GList *list = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
list = g_hash_table_get_values(htable);
g_list_foreach(list, _test_hash_list_foreach_func, NULL);
g_list_free(list);
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/contains", test_hash_contains);
g_test_add_func ("/hash/search/lookup", test_hash_lookup);
g_test_add_func ("/hash/search/lookup-extended", test_hash_lookup_extended);
g_test_add_func ("/hash/search/find", test_hash_find);
g_test_add_func ("/hash/search/get-keys", test_hash_get_keys);
g_test_add_func ("/hash/search/get-values", test_hash_get_values);
return g_test_run();
}
运行结果:
/hash/search/contains:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
key2 is in hash table
OK
/hash/search/lookup:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
key[1] value is NULL
key[2] value is value2
key9 value is NULL
OK
/hash/search/lookup-extended:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
key1 found, ori_key: key1, value: (null)
key9 not found
OK
/hash/search/find:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
key2 found in hash table, value: value2
key9 not found in hash table
OK
/hash/search/get-keys:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
list data: key2
list data: key1
list data: key0
list data: key3
OK
/hash/search/get-values:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
list data: value2
list data: (null)
list data: value0
list data: value3
OK
删除
// 删除一个节点,当hash表创建时使用的是g_hash_table_new_full函数,且已经设置了key或value节点释放函数,如果删除一个节点成功,则会调到释放函数。
// 如果没有设置key或value节点释放函数,则可以先使用g_hash_table_lookup_extended找到该节点,并取出其key和value值,remove方法删除后再对节点进行释放。
gboolean g_hash_table_remove ()
// 从hash表删除一个节点,即使设置了key或value节点释放函数,节点被删除时也不会释放,所以节点需要用户自行释放。
// 由于不会调用到key或value节点释放函数,可以先使用g_hash_table_lookup_extended找到该节点,并取出其key和value值,steal方法删除后再对节点进行释放。
gboolean g_hash_table_steal ()
// 对每个节点调用一次用户传入的GHRFunc函数,如果GHRFunc返回TRUE,则删除该节点,相应的key或value节点释放函数也会被调到。
guint g_hash_table_foreach_remove ()
// 与g_hash_table_foreach_remove类似,但删除节点时,key或value节点释放函数不会被调到。需要用户自行释放删除的节点。
guint g_hash_table_foreach_steal ()
// 删除hash表中所有节点,如果已设置key或value节点释放函数,则每对一个节点都会调用这两个释放函数,释放内存。
void g_hash_table_remove_all ()
// 与g_hash_table_remove_all类似,但不会调到节点释放函数。需要用户自行释放被删除的节点。
void g_hash_table_steal_all ()
删除功能的示例代码如下:
源码见glib_examples\glib_hash\glib_hash_remove
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_print("free key: %s \n", (gchar *)data);
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_print("free value: %s \n", (gchar *)data);
g_free(data);
}
}
static void test_hash_remove(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_remove(htable, key[TEST_ARRAY_LEN/2]);
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "remove");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
static gboolean _test_hash_foreach_remove_ghrfunc
(gpointer key, gpointer value, gpointer user_data)
{
if((0 == g_strcmp0("key1", (const char *)key))
|| (0 == g_strcmp0("key3", (const char *)key)) ) {
return TRUE;
}
return FALSE;
}
static void test_hash_foreach_remove(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_foreach_remove(htable, _test_hash_foreach_remove_ghrfunc, NULL);
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "foreach_remove");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
static void test_hash_remove_all(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_remove_all(htable);
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "remove_all");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
static void test_hash_steal(void)
{
gint i = 0;
gboolean ret = FALSE;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
ret = g_hash_table_steal(htable, key[TEST_ARRAY_LEN/2]);
if(TRUE == ret) {
g_print("found key: %s \n", key[TEST_ARRAY_LEN/2]);
g_free(key[TEST_ARRAY_LEN/2]);
g_free(val[TEST_ARRAY_LEN/2]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "steal");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
static gboolean _test_hash_foreach_steal_ghrfunc
(gpointer key, gpointer value, gpointer user_data)
{
if((0 == g_strcmp0("key1", (const char *)key))
|| (0 == g_strcmp0("key3", (const char *)key)) ) {
return TRUE;
}
return FALSE;
}
static void test_hash_foreach_steal(void)
{
gint i = 0;
gboolean ret = FALSE;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
ret = g_hash_table_foreach_steal(htable, _test_hash_foreach_steal_ghrfunc, NULL);
if(2 == ret) {
g_print("steal key1 and key3 \n");
g_free(key[1]);
g_free(val[1]);
g_free(key[3]);
g_free(val[3]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "foreach_steal");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
static void test_hash_steal_all(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_steal_all(htable);
for(i=0; i<TEST_ARRAY_LEN; i++) {
g_free(key[i]);
g_free(val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "steal_all");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/remove", test_hash_remove);
g_test_add_func ("/hash/foreach-remove", test_hash_foreach_remove);
g_test_add_func ("/hash/remove-all", test_hash_remove_all);
g_test_add_func ("/hash/steal", test_hash_steal);
g_test_add_func ("/hash/foreach-steal", test_hash_foreach_steal);
g_test_add_func ("/hash/steal-all", test_hash_steal_all);
return g_test_run();
}
运行结果:
/hash/remove:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
free key: key2
free value: value2
remove key: key3, value: value3
remove key: key0, value: value0
remove key: key1, value: value1
htable length: 3
free key: key3
free value: value3
free key: key0
free value: value0
free key: key1
free value: value1
OK
/hash/foreach-remove:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
free key: key3
free value: value3
free key: key1
free value: value1
foreach_remove key: key0, value: value0
foreach_remove key: key2, value: value2
htable length: 2
free key: key0
free value: value0
free key: key2
free value: value2
OK
/hash/remove-all:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
free key: key3
free value: value3
free key: key0
free value: value0
free key: key1
free value: value1
free key: key2
free value: value2
htable length: 0
OK
/hash/steal:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
found key: key2
steal key: key3, value: value3
steal key: key0, value: value0
steal key: key1, value: value1
htable length: 3
free key: key3
free value: value3
free key: key0
free value: value0
free key: key1
free value: value1
OK
/hash/foreach-steal:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
steal key1 and key3
foreach_steal key: key0, value: value0
foreach_steal key: key2, value: value2
htable length: 2
free key: key0
free value: value0
free key: key2
free value: value2
OK
/hash/steal-all:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
htable length: 0
OK
迭代器
迭代器相关函数如下:
void g_hash_table_iter_init ()
gboolean g_hash_table_iter_next ()
GHashTable * g_hash_table_iter_get_hash_table ()
void g_hash_table_iter_replace ()
void g_hash_table_iter_remove ()
void g_hash_table_iter_steal ()
下面举例说明。
源码见glib_examples\glib_hash\glib_hash_iter
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_iterator(void)
{
gint i = 0;
GHashTableIter iter;
gpointer iter_key = NULL;
gpointer iter_val = NULL;
gpointer k = NULL;
gpointer v = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_iter_init(&iter, htable);
while(g_hash_table_iter_next(&iter, &iter_key, &iter_val)) {
g_print("iter. key:%s value:%s \n", (char *)iter_key, (char *)iter_val);
if(0 == g_strcmp0((char *)iter_key, "key1")) {
g_hash_table_iter_replace(&iter, g_strdup_printf("replace-value"));
}else if(0 == g_strcmp0((char *)iter_key, "key2")) {
g_hash_table_iter_remove(&iter);
} else if(0 == g_strcmp0((char *)iter_key, "key0")) {
g_hash_table_iter_steal(&iter);
g_free(iter_key);
g_free(iter_val);
} else {
//Nothing to do
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "iter-replace");
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/iterator", test_hash_iterator);
return g_test_run();
}
运行结果:
/hash/iterator:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
iter. key:key3 value:value3
iter. key:key0 value:value0
iter. key:key1 value:value1
iter. key:key2 value:value2
iter-replace key: key3, value: value3
iter-replace key: key1, value: replace-value
OK
把while循环内的代码修改一下,再次运行。
源码见glib_examples\glib_hash\glib_hash_iter_mem_leak_err
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_iterator(void)
{
gint i = 0;
GHashTableIter iter;
gpointer iter_key = NULL;
gpointer iter_val = NULL;
gpointer k = NULL;
gpointer v = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_iter_init(&iter, htable);
while(g_hash_table_iter_next(&iter, &iter_key, &iter_val)) {
g_print("iter. key:%s value:%s \n", (char *)iter_key, (char *)iter_val);
if(0 == g_strcmp0((char *)iter_key, "key1")) {
g_hash_table_iter_replace(&iter, g_strdup_printf("replace-value"));
}
if(0 == g_strcmp0((char *)iter_key, "key2")) {
g_hash_table_iter_remove(&iter);
}
if(0 == g_strcmp0((char *)iter_key, "key0")) {
g_hash_table_iter_steal(&iter);
g_free(iter_key);
g_free(iter_val);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "iter-replace");
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/iterator", test_hash_iterator);
return g_test_run();
}
运行结果:
/hash/iterator:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
iter. key:key3 value:value3
iter. key:key0 value:value0
iter. key:key1 value:value1
iter. key:key2 value:value2
iter-replace key: key3, value: value3
iter-replace key: key1, value: replace-value
OK
看上去仍得到了预期的结果,但使用valgrind检查,发现有内存问题。
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_hash_iter_mem_leak_err
==51907== Memcheck, a memory error detector
==51907== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==51907== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==51907== Command: ./glib_hash_iter_mem_leak_err
==51907==
/hash/iterator:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
htable length: 4
iter. key:key3 value:value3
iter. key:key0 value:value0
iter. key:key1 value:value1
iter. key:key2 value:value2
==51907== Invalid read of size 1
==51907== at 0x4C2F1B1: strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==51907== by 0x401058: test_hash_iterator (in /root/glib_examples/glib_hash/glib_hash_iter_mem_leak_err/build/glib_hash_iter_mem_leak_err)
==51907== by 0x4EA3779: test_case_run (gtestutils.c:2158)
==51907== by 0x4EA3779: g_test_run_suite_internal (gtestutils.c:2241)
==51907== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==51907== by 0x4EA3B3D: g_test_run_suite (gtestutils.c:2328)
==51907== by 0x4EA3B60: g_test_run (gtestutils.c:1596)
==51907== by 0x401110: main (in /root/glib_examples/glib_hash/glib_hash_iter_mem_leak_err/build/glib_hash_iter_mem_leak_err)
==51907== Address 0x576e330 is 0 bytes inside a block of size 5 free'd
==51907== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==51907== by 0x400E10: _test_hash_str_key_destroy_func (in /root/glib_examples/glib_hash/glib_hash_iter_mem_leak_err/build/glib_hash_iter_mem_leak_err)
==51907== by 0x4E6EB4F: g_hash_table_remove_node (ghash.c:455)
==51907== by 0x4E6EBCB: iter_remove_or_steal (ghash.c:845)
==51907== by 0x401044: test_hash_iterator (in /root/glib_examples/glib_hash/glib_hash_iter_mem_leak_err/build/glib_hash_iter_mem_leak_err)
==51907== by 0x4EA3779: test_case_run (gtestutils.c:2158)
==51907== by 0x4EA3779: g_test_run_suite_internal (gtestutils.c:2241)
==51907== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==51907== by 0x4EA3B3D: g_test_run_suite (gtestutils.c:2328)
==51907== by 0x4EA3B60: g_test_run (gtestutils.c:1596)
==51907== by 0x401110: main (in /root/glib_examples/glib_hash/glib_hash_iter_mem_leak_err/build/glib_hash_iter_mem_leak_err)
......(以下省略)
根据valgrind输出信息,可以猜测,strcmp访问了已经释放过的内存。
实际上该问题出在以下两行:
if(0 == g_strcmp0((char *)iter_key, "key1")) {
g_hash_table_iter_replace(&iter, g_strdup_printf("replace-value"));
}
if(0 == g_strcmp0((char *)iter_key, "key2")) {
g_hash_table_iter_remove(&iter);
}
g_hash_table_iter_replace调用之后,iter_key和iter_key都会被释放,后面就不能再次访问iter_key这段内存了。如果访问该段内存,相当于使用了野指针。
Hash函数
gboolean g_direct_equal ()
guint g_direct_hash ()
gboolean g_int_equal ()
guint g_int_hash ()
gboolean g_int64_equal ()
guint g_int64_hash ()
gboolean g_double_equal ()
guint g_double_hash ()
gboolean g_str_equal ()
guint g_str_hash ()
上述hash函数的类型是指key的数据类型,value的值可以随意。
除了以上几种key类型,GLib还支持以下类型的key。
GString类型的key:
g_string_equal
g_string_hash
GBytes类型的key:
g_bytes_equal
g_bytes_hash
GVariant类型的key:
g_variant_equal
g_variant_hash
专题
Hash表数据存储方式
GHashTable是一个不透明数据结构,无法像链表队列那样查看其内部结构,但仍可以使用分析链表节点内存的方式分析GHashTable节点的内存分布情况。
在下面的例子中,首先将字符串型hash插入不同的key和value,遍历一遍,然后将传入的key和value值修改,再遍历,即可得知GHashTable在插入节点时是自己创建了内存空间还是使用外部的内存空间。
源码见glib_examples\glib_hash\glib_hash_mem
#include <glib.h>
#define TEST_ARRAY_LEN 10
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void test_hash_basic(void)
{
gint i = 0;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_hash_table_insert(htable, key[i], val[i]);
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i][0] = 'K';
val[i][0] = 'V';
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "change");
g_print("htable length: %d \n", g_hash_table_size(htable));
g_hash_table_destroy(htable);
for(i=0; i<TEST_ARRAY_LEN; i++) {
g_free(key[i]);
g_free(val[i]);
}
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/basic", test_hash_basic);
return g_test_run();
}
运行结果:
/hash/basic:
ori key: key0, value: value0
ori key: key1, value: value1
ori key: key2, value: value2
ori key: key3, value: value3
ori key: key4, value: value4
ori key: key5, value: value5
ori key: key6, value: value6
ori key: key7, value: value7
ori key: key8, value: value8
ori key: key9, value: value9
change key: Key0, value: Value0
change key: Key1, value: Value1
change key: Key2, value: Value2
change key: Key3, value: Value3
change key: Key4, value: Value4
change key: Key5, value: Value5
change key: Key6, value: Value6
change key: Key7, value: Value7
change key: Key8, value: Value8
change key: Key9, value: Value9
htable length: 10
OK
结果改变,说明Hash表创建时,只会将内部的key和value的指针指向传入的参数,不会真正拷贝内存。因为使用的是外部的内存空间,需要特别注意在Hash表使用过程中传入变量的生命周期,这些变量的内存不可随便释放。
将hash表键值保存到字符串数组
// 获取hash表中的所有key,并将其放到一个字符串数组中。
// 第二个参数length的作用是返回实际的key个数,因为key的值有可能为空,如果key为空,没有length这个参数就无法得知数组的真正长度。
gpointer * g_hash_table_get_keys_as_array ()
先来看一个错误示例(运行时会有段错误):
源码见glib_examples\glib_hash\glib_hash_key_to_array_err
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_get_keys_as_array(void)
{
gint i = 0;
guint length = 0;
gchar **strv = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
strv = (gchar **)g_hash_table_get_keys_as_array(htable, &length);
for(i=0;i<length;i++) {
g_print("strv[%d]: %s \n", i, strv[i]);
}
g_strfreev(strv);
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/get-keys-as-array", test_hash_get_keys_as_array);
return g_test_run();
}
运行结果:
/hash/search/get-keys-as-array:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0]: key3
strv[1]: key0
strv[2]: key1
strv[3]: key2
段错误 (核心已转储)
使用valgrind运行一下。
[root@centos7_6 build]# valgrind --tool=memcheck --leak-check=full ./glib_hash_key_to_array_err
==49979== Memcheck, a memory error detector
==49979== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==49979== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==49979== Command: ./glib_hash_key_to_array_err
==49979==
/hash/search/get-keys-as-array:
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0]: key3
strv[1]: key0
strv[2]: key1
strv[3]: key2
==49979== Invalid free() / delete / delete[] / realloc()
==49979== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==49979== by 0x400CB0: _test_hash_str_key_destroy_func (in /root/glib_examples/glib_hash/glib_hash_key_to_array_err/build/glib_hash_key_to_array_err)
==49979== by 0x4E6EFA3: g_hash_table_remove_all_nodes.part.0 (ghash.c:545)
==49979== by 0x4E6FDBA: g_hash_table_remove_all_nodes (ghash.c:1426)
==49979== by 0x4E6FDBA: g_hash_table_remove_all (ghash.c:1429)
==49979== by 0x4E6FDFD: g_hash_table_destroy (ghash.c:1122)
==49979== by 0x400EC0: test_hash_get_keys_as_array (in /root/glib_examples/glib_hash/glib_hash_key_to_array_err/build/glib_hash_key_to_array_err)
==49979== by 0x4EA3779: test_case_run (gtestutils.c:2158)
==49979== by 0x4EA3779: g_test_run_suite_internal (gtestutils.c:2241)
==49979== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==49979== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==49979== by 0x4EA3B3D: g_test_run_suite (gtestutils.c:2328)
==49979== by 0x4EA3B60: g_test_run (gtestutils.c:1596)
==49979== by 0x400F03: main (in /root/glib_examples/glib_hash/glib_hash_key_to_array_err/build/glib_hash_key_to_array_err)
==49979== Address 0x576f120 is 0 bytes inside a block of size 5 free'd
==49979== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==49979== by 0x4E9E098: g_strfreev (gstrfuncs.c:2487)
==49979== by 0x400EB4: test_hash_get_keys_as_array (in /root/glib_examples/glib_hash/glib_hash_key_to_array_err/build/glib_hash_key_to_array_err)
==49979== by 0x4EA3779: test_case_run (gtestutils.c:2158)
==49979== by 0x4EA3779: g_test_run_suite_internal (gtestutils.c:2241)
==49979== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==49979== by 0x4EA392D: g_test_run_suite_internal (gtestutils.c:2253)
==49979== by 0x4EA3B3D: g_test_run_suite (gtestutils.c:2328)
==49979== by 0x4EA3B60: g_test_run (gtestutils.c:1596)
==49979== by 0x400F03: main (in /root/glib_examples/glib_hash/glib_hash_key_to_array_err/build/glib_hash_key_to_array_err)
==49979==
......(以下省略)
可以看出是在g_strfreev和_test_hash_str_key_destroy_func两处出错,Invalid free(),因此有理由怀疑是对同一内存进行了多次释放。下面这两个地址打印出来,看是否相同。
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_key_to_array_addr_err
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_get_keys_as_array(void)
{
gint i = 0;
guint length = 0;
gchar **strv = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_print("key[%d](addr:%p): %s \n", i, key[i], key[i]);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
strv = (gchar **)g_hash_table_get_keys_as_array(htable, &length);
for(i=0;i<length;i++) {
g_print("strv[%d](addr:%p): %s \n", i, strv[i], strv[i]);
}
g_strfreev(strv);
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/get-keys-as-array", test_hash_get_keys_as_array);
return g_test_run();
}
运行结果:
/hash/search/get-keys-as-array:
key[0](addr:0x2210f70): key0
key[1](addr:0x22120a0): key1
key[2](addr:0x22120c0): key2
key[3](addr:0x22121f0): key3
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0](addr:0x22121f0): key3
strv[1](addr:0x2210f70): key0
strv[2](addr:0x22120a0): key1
strv[3](addr:0x22120c0): key2
段错误 (核心已转储)
与猜测一致,g_hash_table_get_keys_as_array并不会将key的值重新复制一份,其内容只是指向各个key的指针,内存还在hash表中。
对于上述程序段错误,有至少以下三种方法可以修复。
方法一:在g_hash_table_new_full时不指定key的释放函数,得到key的数组后使用g_strfreev释放。
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_key_to_array1
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_get_keys_as_array(void)
{
gint i = 0;
guint length = 0;
gchar **strv = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
NULL, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_print("key[%d](addr:%p): %s \n", i, key[i], key[i]);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
strv = (gchar **)g_hash_table_get_keys_as_array(htable, &length);
for(i=0;i<length;i++) {
g_print("strv[%d](addr:%p): %s \n", i, strv[i], strv[i]);
}
g_strfreev(strv);
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/get-keys-as-array", test_hash_get_keys_as_array);
return g_test_run();
}
运行结果:
/hash/search/get-keys-as-array:
key[0](addr:0xe3ff70): key0
key[1](addr:0xe410a0): key1
key[2](addr:0xe410c0): key2
key[3](addr:0xe411f0): key3
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0](addr:0xe411f0): key3
strv[1](addr:0xe3ff70): key0
strv[2](addr:0xe410a0): key1
strv[3](addr:0xe410c0): key2
OK
方法二:在g_hash_table_new_full时仍然指定key的释放函数,不使用g_strfreev释放key的数组,直接释放一级指针。
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_key_to_array2
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_get_keys_as_array(void)
{
gint i = 0;
guint length = 0;
gchar **strv = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_print("key[%d](addr:%p): %s \n", i, key[i], key[i]);
if(1 == i) {
g_free(val[i]);
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
strv = (gchar **)g_hash_table_get_keys_as_array(htable, &length);
for(i=0;i<length;i++) {
g_print("strv[%d](addr:%p): %s \n", i, strv[i], strv[i]);
}
g_free(strv);
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/get-keys-as-array", test_hash_get_keys_as_array);
return g_test_run();
}
运行结果:
/hash/search/get-keys-as-array:
key[0](addr:0x15a8f70): key0
key[1](addr:0x15aa0a0): key1
key[2](addr:0x15aa0c0): key2
key[3](addr:0x15aa1f0): key3
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0](addr:0x15aa1f0): key3
strv[1](addr:0x15a8f70): key0
strv[2](addr:0x15aa0a0): key1
strv[3](addr:0x15aa0c0): key2
OK
方法三:在节点destroy前,先调用g_hash_table_steal_all,将所有的节点从hash表移出。将得到的key数组使用g_strfreev释放,value分别使用g_free释放。
示例代码如下:
源码见glib_examples\glib_hash\glib_hash_key_to_array3
#include <glib.h>
#define TEST_ARRAY_LEN 4
static void _test_hash_str_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
g_print("%s key: %s, value: %s \n", (gchar *)user_data, (gchar *)key, (gchar *)value);
return;
}
static void _test_hash_str_key_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void _test_hash_str_value_destroy_func(gpointer data)
{
if(NULL != data) {
g_free(data);
}
}
static void test_hash_get_keys_as_array(void)
{
gint i = 0;
guint length = 0;
gchar **strv = NULL;
char *key[TEST_ARRAY_LEN] = {NULL};
char *val[TEST_ARRAY_LEN] = {NULL};
GHashTable *htable = NULL;
g_print("\n");
htable = g_hash_table_new_full(g_str_hash, g_str_equal, \
_test_hash_str_key_destroy_func, _test_hash_str_value_destroy_func);
for(i=0; i<TEST_ARRAY_LEN; i++) {
key[i] = g_strdup_printf("key%d", i);
val[i] = g_strdup_printf("value%d", i);
g_print("key[%d](addr:%p): %s \n", i, key[i], key[i]);
if(1 == i) {
g_free(val[i]);
val[i] = NULL;
g_hash_table_insert(htable, key[i], NULL);
} else {
g_hash_table_insert(htable, key[i], val[i]);
}
}
g_hash_table_foreach(htable, _test_hash_str_foreach_func, "ori");
g_print("htable length: %d \n", g_hash_table_size(htable));
strv = (gchar **)g_hash_table_get_keys_as_array(htable, &length);
for(i=0;i<length;i++) {
g_print("strv[%d](addr:%p): %s \n", i, strv[i], strv[i]);
}
g_hash_table_steal_all(htable);
g_strfreev(strv);
for(i=0;i<TEST_ARRAY_LEN;i++) {
if(NULL != val[i]) {
g_free(val[i]);
}
}
g_hash_table_destroy(htable);
return;
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func ("/hash/search/get-keys-as-array", test_hash_get_keys_as_array);
return g_test_run();
}
运行结果:
/hash/search/get-keys-as-array:
key[0](addr:0x1758f70): key0
key[1](addr:0x175a0a0): key1
key[2](addr:0x175a0c0): key2
key[3](addr:0x175a1f0): key3
ori key: key3, value: value3
ori key: key0, value: value0
ori key: key1, value: (null)
ori key: key2, value: value2
htable length: 4
strv[0](addr:0x175a1f0): key3
strv[1](addr:0x1758f70): key0
strv[2](addr:0x175a0a0): key1
strv[3](addr:0x175a0c0): key2
OK