C 释放已用数据的内存会导致分段错误
我编写了一个哈希表,它基本上由以下两种结构组成:C 释放已用数据的内存会导致分段错误,c,pointers,hashtable,resize,C,Pointers,Hashtable,Resize,我编写了一个哈希表,它基本上由以下两种结构组成: 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是一个多维数组,其中包
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;
}
您正在释放传递给函数的指针。只有当您知道调用您函数的人不再尝试使用旧值
d
时,这才是安全的。检查所有调用\u dictRehash()
的代码,确保旧指针上没有挂起任何东西
free(d)
时,您希望对struct dict
调用更多的析构函数
,这将在内部释放分配给指向dictEntry的指针的内存
next
指针,为什么不直接向它添加新的散列项呢d
,而是通过分配更多struct dictEntry
并将它们分配给适当的next
来扩展d
收缩
d
时,您必须迭代next
以到达末尾,然后开始释放d
中struct dictEntry
s的内存 dictCreate实际上做什么
我认为您正在混淆(固定大小)dict
对象和dict.table
中指向dictEntries
的指针数组(可能大小可变)
也许您可以只
realloc()
dict.table指向的内存,而不是创建一个新的“dict”对象并释放旧的对象(顺便说一句,这并不是释放dict条目表!)为了阐明Graham的观点,您需要注意如何在这个库中访问内存。用户有一个指向其字典的指针。重新刷新时,释放该指针引用的内存。虽然您为他们分配了一个新字典,但新指针永远不会返回给他们,因此他们不知道不使用旧的字典。当他们再次尝试访问字典时,它指向已释放的内存
一种可能性是不完全扔掉旧字典,而只扔掉字典中分配的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;
}
这有点离经叛道。你得到一个散列值,然后用表的大小按位进行and运算,我想这在某种意义上是有效的,它保证在(我想?
[0,max_size)
之内,我想你可能是指模的%
。不清楚;你的问题是“我为什么会出现seg错误?”?到处使用下划线会让你的代码很难阅读。是的,事实上,这就是为什么我会出现seg错误!会尽快改变。dictCreate做什么,然后?发布代码;-)嗯,我调用dictAdd(d,key,value),它会检查哈希表是否已满,然后调用dictRehash(),这可能是问题吗?是的,除非dictAdd()
将新的dict指针返回给它的调用者。我避免使用realloc,因为它应该非常慢。但是,我知道这些条目并没有被释放-指针只是在新的哈希表中再次使用。这被称为“过早优化”。无论如何,目前速度是您最不担心的。谢谢,您的第一点是sol注意。扩大列表并不是你想要的行为。如果这能解决你的问题,投票并接受答案会很好:DIt还没有完全解决。如果我不能释放旧的dict,这就是内存泄漏,对吗?有什么解决方案可以避免这一点?我想这是非常正确的-但还有另一个问题:\u dictAddRaw
补充道整个条目链,因为条目的next
字段永远不会设置为NULL(注意它为NULL的位置,因为在for
循环中需要它。因此重建的表将是一堆相互链接的链…还有&(size-1)
假设表格大小始终为2的幂,则应能正常工作。但模数更好!
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));