我写了一个哈希表,它基本上由这两个结构组成:
typedef struct dictEntry {
void *key;
void *value;
struct dictEntry *next;
} dictEntry;
typedef struct dict {
dictEntry **table;
unsigned long size;
unsigned long items;
} dict;
dict.table
是一个多维数组,它包含所有存储的键/值对,它们也是一个链表。
如果散列表的一半已满,我会通过将大小加倍并重新散列来扩展它:
dict *_dictRehash(dict *d) {
int i;
dict *_d;
dictEntry *dit;
_d = dictCreate(d->size * 2);
for (i = 0; i < d->size; i++) {
for (dit = d->table[i]; dit != NULL; dit = dit->next) {
_dictAddRaw(_d, dit);
}
}
/* FIXME memory leak because the old dict can never be freed */
free(d); // seg fault
return _d;
}
上面的函数使用旧哈希表中的指针并将其存储在新创建的哈希表中。释放旧的dict d
时会发生分段错误。
如何在不必再次为键/值对分配内存的情况下释放旧的哈希表结构?
编辑,以获取完整性:
dict *dictCreate(unsigned long size) {
dict *d;
d = malloc(sizeof(dict));
d->size = size;
d->items = 0;
d->table = calloc(size, sizeof(dictEntry*));
return d;
}
void dictAdd(dict *d, void *key, void *value) {
dictEntry *entry;
entry = malloc(sizeof *entry);
entry->key = key;
entry->value = value;
entry->next = '\0';
if ((((float)d->items) / d->size) > 0.5) d = _dictRehash(d);
_dictAddRaw(d, entry);
}
void _dictAddRaw(dict *d, dictEntry *entry) {
int index = (hash(entry->key) & (d->size - 1));
if (d->table[index]) {
dictEntry *next, *prev;
for (next = d->table[index]; next != NULL; next = next->next) {
prev = next;
}
prev->next = entry;
} else {
d->table[index] = entry;
}
d->items++;
}
答案 0 :(得分:3)
但是给你一些看法:
当你free(d)
期望destructor
更多struct dict
来自dictEntry
时,会在内部释放分配给指向next
指针的指针的内存
为什么要删除整个has表来展开它?你有一个d
指针,为什么不直接添加新的哈希条目呢?
解决方案不是释放d
,而是通过分配更多struct dictEntry
并将其分配给适当的next
来展开d
。
签约next
时,您必须迭代struct dictEntry
才能到达终点,然后开始释放d
内{{1}}内存的内存。
答案 1 :(得分:3)
为了澄清格雷厄姆的观点,你需要注意在这个库中如何访问内存。用户有一个指向其字典的指针。重新散列时,释放该指针引用的内存。虽然您为它们分配了一个新字典,但新指针永远不会返回给它们,因此它们不知道不使用旧字典。当他们再次尝试访问他们的字典时,它指向释放的内存。
一种可能性是不要完全丢弃旧字典,而只删除在字典中分配的dictEntry表。这样,您的用户将永远不必更新其指针,但您可以重新调整表的大小以适应更高效的访问。尝试这样的事情:
void _dictRehash(dict *d) {
printf("rehashing!\n");
int i;
dictEntry *dit;
int old_size = d->size;
dictEntry** old_table = d->table;
int size = old_size * 2;
d->table = calloc(size, sizeof(dictEntry*));
d->size = size;
d->items = 0;
for (i = 0; i < old_size; i++) {
for (dit = old_table[i]; dit != NULL; dit = dit->next) {
_dictAddRaw(d, dit);
}
}
free(old_table);
return;
}
作为旁注,我不确定你的哈希函数是做什么的,但在我看来这行
int index = (hash(entry->key) & (d->size - 1));
有点不正统。你得到一个哈希值,然后做一个按位和表的大小,我觉得它可以保证在它内部(我认为?)[0, max_size)
,我想你可能意味着{{} 1}}表示模数。
答案 2 :(得分:1)
您正在释放传入您的函数的指针。只有当您知道调用您的函数的任何人仍在尝试使用d
的旧值时,这才是安全的。检查调用_dictRehash()
的所有代码,并确保没有任何内容挂在旧指针上。
答案 3 :(得分:1)
dictCreate
实际上做了什么?
我认为你在(固定大小)dict
对象与dictEntries
中指向dict.table
的(可能是可变大小的)指针数组之间感到困惑。
也许你只能realloc()
dict.table
所指向的内存,而不是创建一个新的'dict'对象并释放旧的对象(顺便说一句,这并不是在释放指责表。 !)