C 无法调整哈希表的大小

Posted

技术标签:

【中文标题】C 无法调整哈希表的大小【英文标题】:C Having Trouble Resizing a Hash Table 【发布时间】:2012-10-14 16:20:58 【问题描述】:

我将在此处发布(我认为)与问题相关的代码的 sn-ps,但如有必要,我可以粘贴 bin。可能已经发布了足够多的代码:P

我的程序包含一个哈希表,当某个哈希桶达到 20 个条目时,该哈希表需要加倍。虽然我相信逻辑很好,而且它像魅力一样编译,但它抛出了一个 Segmentation Fault。代码在不调整大小时运行起来就像一个魅力,但调整大小却把事情搞砸了。

感谢您的帮助:)

错误

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401012 in ml_add (ml=0x7fffffffe528, me=0x75a5a0) at mlist.c:74
74          while((cursorNode->next) != NULL)
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.80.el6_3.5.x86_64
(gdb) backtrace
#0  0x0000000000401012 in ml_add (ml=0x7fffffffe528, me=0x75a5a0) at mlist.c:74
#1  0x0000000000401554 in main (argc=1, argv=0x7fffffffe638) at finddupl.c:39

哈希表的结构

typedef struct bN  //linked list node containing data and next
    MEntry *nestedEntry;
    struct bN *next;
 bucketNode;

typedef struct bL  // bucket as linked list
    struct bN *first;
    int bucketSize;
 bucket;

struct mlist 
    struct bL *currentTable; //bucket array
;

添加功能

int ml_add(MList **ml, MEntry *me)

    MList *tempList;
    tempList = *ml;

    bucketNode *tempNode = (bucketNode *)malloc(sizeof(bucketNode));
    tempNode->nestedEntry = me;
    tempNode->next = NULL;

    unsigned long currentHash = me_hash(me, tableSize);

    if((tempList->currentTable[currentHash].bucketSize) == 0)   
        tempList->currentTable[currentHash].first = tempNode;
        tempList->currentTable[currentHash].bucketSize = (tempList->currentTable[currentHash].bucketSize) + 1;
    
    else if((tempList->currentTable[currentHash].bucketSize) == 20)
        printf("About to resize");
        printf("About to resize");
        tempList = ml_resize(&tempList, (tableSize * 2));
        tableSize = tableSize * 2;
        ml_add(&tempList,me);
    
    else
        bucketNode *cursorNode;
        cursorNode = tempList->currentTable[currentHash].first;
        while((cursorNode->next) != NULL)
            cursorNode = cursorNode->next;
        
        cursorNode->next = tempNode;
        tempList->currentTable[currentHash].bucketSize = (tempList->currentTable[currentHash].bucketSize) + 1;
        return 1;
    

    return 1;


调整大小功能

MList *ml_resize(MList **ml, int newSize)
    MList *oldList;
    oldList = *ml;

    MList *newList;

    if ((newList = (MList *)malloc(sizeof(MList))) != NULL)
        newList->currentTable = (bucket *)malloc(newSize * sizeof(bucket));
        int i;
        for(i = 0; i < newSize; i++)
            newList->currentTable[i].first = NULL;
            newList->currentTable[i].bucketSize = 0;
        
    

    int j;
    for(j = 0; j < tableSize; j++)
        bucketNode *cursorNode = oldList->currentTable[j].first;
        bucketNode *nextNode;
        while(cursorNode != NULL)
            nextNode = cursorNode->next;
            ml_transfer(&newList, cursorNode, newSize); 
            cursorNode = nextNode;  
        
    

    free(oldList);

    return newList;

转移到新列表功能

void ml_transfer(MList **ml, bucketNode *insertNode, int newSize)

    MList *newList;
    newList = *ml;

    bucketNode *tempNode = insertNode;

    tempNode->next = NULL;

    unsigned long currentHash = me_hash((tempNode->nestedEntry), newSize);

    if((newList->currentTable[currentHash].bucketSize) == 0)    
        newList->currentTable[currentHash].first = tempNode;
        newList->currentTable[currentHash].bucketSize = (newList->currentTable[currentHash].bucketSize) + 1;
    
    else
        bucketNode *cursorNode;
        cursorNode = newList->currentTable[currentHash].first;
        while((cursorNode->next) != NULL)
            cursorNode = cursorNode->next;
        
        cursorNode->next = tempNode;
        newList->currentTable[currentHash].bucketSize = (newList->currentTable[currentHash].bucketSize) + 1;
    


【问题讨论】:

我才刚刚开始使用 C。我对它有一点了解,但不确定我将如何在这种情况下准确地应用它 :) 我没有看到错误.. 但作为旁注,如果您将新节点插入到列表的前面,则不必在列表的末尾插入它们这是一个 O(1) 操作而不是 O(n) 操作,并且需要更少的代码:) 你能断言me_hash() 函数将始终返回一个介于 0 和 newSize -1 之间的值吗? 与段错误问题无关,但在您当前的ml_add() 实现中存在内存泄漏。请注意hashtable需要调整大小时的代码流程,首先调整hashtable的大小,然后递归调用ml_add()在调整大小的Hashtable上插入新节点;但是您不要在 tempNode 上调用 free()。递归调用将重新创建一个新的 tempNode,而原来的 tempNode 将丢失,从而造成内存泄漏。 我还认为您在 ml_resize() 中泄漏了内存。当你释放oldList时,你应该先释放oldList-&gt;currentTable。我建议创建一个函数来封装 MList 实例的“破坏”以避免此类问题。 【参考方案1】:

问题很可能在于 ml_add() 函数在调整哈希表大小时无法更新 MList** ml 参数节点。

当哈希表被调整大小时,旧的哈希表被销毁(内部,ml_resize()),但指向调整大小的新哈希表的指针只是在 tempList 变量中更新,这只是 *ml 的本地副本。您还应该更新 *ml 以修改在函数外部保持对 hashTable 的引用的变量,否则,它会指向已删除的无效 Hashtable。尝试以下修改:

...
else if((tempList->currentTable[currentHash].bucketSize) == 20)
        printf("About to resize");
        printf("About to resize");
        tempList = ml_resize(&tempList, (tableSize * 2));
        tableSize = tableSize * 2;
        ml_add(&tempList,me);
        *ml = tempList;   // this is necesary to fix the pointer outside the
                           // function, that still points to the hashtable 
                           // memory freed by the resize function

...

另外请注意我所做的关于您的代码中存在两个内存泄漏的 cmets,并且我还将考虑 @hexist 指出的没有必要在头部的喜欢列表的末尾插入,从而简化代码并使其更快。

【讨论】:

我这样做了,但没有成功。但是后来我将它与我的存储桶的 calloc'ing 混合在一起,而不是 malloc'ing(之前没有做任何事情),然后一切都奏效了! :D 谢谢 :)

以上是关于C 无法调整哈希表的大小的主要内容,如果未能解决你的问题,请参考以下文章

哈希表 - 调整崩溃程序的大小

为啥将地址右移三位作为固定大小哈希表的哈希函数?

是否可以在 Perl 中保留哈希表的大小?

哈希表哈希桶的实现

为啥哈希表的大小为 127(素数)优于 128?

哈希表调整大小:我们如何在不知道密钥的情况下做到这一点?