简体   繁体   English

释放已使用数据的内存会导致分段错误

[英]Freeing memory of used data leads to Segmentation Fault

I wrote a hashtable and it basically consists of these two structures: 我写了一个哈希表,它基本上由这两个结构组成:

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 is a multidimensional array, which contains all the stored key/value pair, which again are a linked list. dict.table是一个多维数组,它包含所有存储的键/值对,它们也是一个链表。

If half of the hashtable is full, I expand it by doubling the size and rehashing it: 如果哈希表的一半已满,我会通过将大小加倍并重新散列来扩展它:

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;
}

The function above uses the pointers from the old hash table and stores it in the newly created one. 上面的函数使用旧哈希表中的指针并将其存储在新创建的哈希表中。 When freeing the old dict d a Segmentation Fault occurs. 释放旧dict d时会发生分段错误。

How am I able to free the old hashtable struct without having to allocate the memory for the key/value pairs again? 如何在不必再次为键/值对分配内存的情况下释放旧的哈希表结构?

Edit, for completness: 编辑,以获得完整性:

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++;
}
  1. best way to debug this is to run your code against valgrind . 调试它的最佳方法是针对valgrind运行代码。

But to you give some perspective : 但是给你一些看法:

  1. when you free(d) you are expecting more of a destructor call on your struct dict which would internally free the memory allocated to the pointer to pointer to dictEntry 当你free(d)你期望在你的struct dict上有更多的destructor调用,它会在内部释放分配给指向dictEntry的指针的内存

  2. why do you have to delete the entire has table to expand it ? 为什么你要删除整个has表来扩展它? you have a next pointer anyways why not just append new hash entries to it ? 你有一个next指针,为什么不直接添加新的哈希条目呢?

Solution is not to free the d rather just expand the d by allocating more struct dictEntry and assigning them to appropriate next . 解决方案不是释放d而是通过分配更多struct dictEntry并将它们分配给适当的next来扩展d

When contracting the d you will have to iterate over next to reach the end and then start freeing the memory for struct dictEntry s inside of your d . 当收缩d你将不得不遍历next到达终点,然后开始释放的内存struct dictEntry是你的内部d

To clarify Graham's point, you need to pay attention to how memory is being accessed in this library. 为了澄清格雷厄姆的观点,你需要注意在这个库中如何访问内存。 The user has one pointer to their dictionary. 用户有一个指向其字典的指针。 When you rehash, you free the memory referenced by that pointer. 重新散列时,释放该指针引用的内存。 Although you allocated a new dictionary for them, the new pointer is never returned to them, so they don't know not to use the old one. 虽然您为它们分配了一个新字典,但新指针永远不会返回给它们,因此它们不知道不使用旧字典。 When they try to access their dictionary again, it's pointing to freed memory. 当他们再次尝试访问他们的字典时,它指向释放的内存。

One possibility is not to throw away the old dictionary entirely, but only the dictEntry table you allocated within the dictionary. 一种可能性是不要完全丢弃旧字典,而只删除在字典中分配的dictEntry表。 That way your users will never have to update their pointer, but you can rescale the table to accomodate more efficient access. 这样,您的用户将永远不必更新其指针,但您可以重新调整表的大小以适应更高效的访问。 Try something like this: 尝试这样的事情:

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;

}

As a side note, I'm not sure what your hash function does, but it seems to me that the line 作为旁注,我不确定你的哈希函数是做什么的,但在我看来,这条线

int index = (hash(entry->key) & (d->size - 1));

is a little unorthodox. 有点不正统。 You get a hash value and do a bitwise and with the size of the table, which I guess works in the sense that it will be guaranteed to be within (I think?) [0, max_size) , I think you might mean % for modulus. 你得到一个哈希值,然后做一个按位和表的大小,我猜这是有效的,因为它将保证在(我认为?) [0, max_size) ,我想你可能意味着%模量。

You are freeing a pointer which is passed in to your function. 你正在释放一个传入你的函数的指针。 This is only safe if you know that whoever's calling your function isn't still trying to use the old value of d . 只有当你知道调用你的函数的人仍然没有尝试使用旧的d值时,这才是安全的。 Check all the code which calls _dictRehash() and make sure nothing's hanging on to the old pointer. 检查所有调用_dictRehash()的代码,确保没有任何内容挂在旧指针上。

What does dictCreate actually do? dictCreate实际上做了什么?

I think you're getting confused between the (fixed size) dict object, and the (presumably variable sized) array of pointers to dictEntries in dict.table . 我认为你在(固定大小) dict对象和dictEntriesdict.table指针(可能是可变大小的)数组之间dictEntries dict.table

Maybe you could just realloc() the memory pointed to by dict.table , rather than creating a new 'dict' object and freeing the old one (which incidentally, isn't freeing the table of dictentries anyway!) 也许你可以只是realloc() dict.table指向的内存,而不是创建一个新的'dict'对象并释放旧的对象(顺便说一句,无论如何都不会释放指责表!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM