[英]Freeing memory of used data leads to Segmentation Fault
我寫了一個哈希表,它基本上由這兩個結構組成:
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++;
}
但是給你一些看法:
當你free(d)
你期望在你的struct dict
上有更多的destructor
調用,它會在內部釋放分配給指向dictEntry
的指針的內存
為什么你要刪除整個has表來擴展它? 你有一個next
指針,為什么不直接添加新的哈希條目呢?
解決方案不是釋放d
而是通過分配更多struct dictEntry
並將它們分配給適當的next
來擴展d
。
當收縮d
你將不得不遍歷next
到達終點,然后開始釋放的內存struct dictEntry
是你的內部d
。
為了澄清格雷厄姆的觀點,你需要注意在這個庫中如何訪問內存。 用戶有一個指向其字典的指針。 重新散列時,釋放該指針引用的內存。 雖然您為它們分配了一個新字典,但新指針永遠不會返回給它們,因此它們不知道不使用舊字典。 當他們再次嘗試訪問他們的字典時,它指向釋放的內存。
一種可能性是不要完全丟棄舊字典,而只刪除在字典中分配的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)
,我想你可能意味着%
模量。
你正在釋放一個傳入你的函數的指針。 只有當你知道調用你的函數的人仍然沒有嘗試使用舊的d
值時,這才是安全的。 檢查所有調用_dictRehash()
的代碼,確保沒有任何內容掛在舊指針上。
dictCreate
實際上做了什么?
我認為你在(固定大小) dict
對象和dictEntries
中dict.table
指針(可能是可變大小的)數組之間dictEntries
dict.table
。
也許你可以只是realloc()
dict.table
指向的內存,而不是創建一個新的'dict'對象並釋放舊的對象(順便說一句,無論如何都不會釋放指責表!)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.