【问题标题】:hashtable issue in C; double free detected in tcache 2C中的哈希表问题;在 tcache 2 中检测到双重释放
【发布时间】:2022-01-05 16:22:26
【问题描述】:

我正在用单链接列表编写一个哈希表,我有这个问题free(): double free detected in tcache 2 我试图修复它但没有成功,问题是free(),所以你能解释一下我为什么会有它,因此,如果有人可以提供帮助,请帮助我,我正在尝试修复它几个小时... 谢谢。

我在 youtube 上观看了一些视频,并在网站和此处观看了许多主题,但我没有找到适合我的解决方案。

这是我的功能:

/**List header */
#ifndef LISTE_H
#define LISTE_H

struct _list_node {
    void * data;
    struct _list_node *next;
};

typedef struct _list_node s_node;

s_node * list_create(void);
void * list_get_data(s_node * node);
void list_set_data(s_node * node, void * data);
s_node * list_insert(s_node * head, void * data);
s_node * list_append(s_node * head, void * data);
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
    void * param, s_node ** last);
s_node * list_ordered_append(s_node ** head, int (*fct)(s_node * node, void * param),
    void * param);
s_node * list_remove (s_node * head, void * data);
s_node * list_headRemove(s_node * head);
void * list_destroy(s_node * head);
void afficher_s_node(s_node * list);
int list_is_empty( s_node * node );
unsigned int list_size(s_node * node);
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
    void * param, s_node ** last);

#endif

/****  c file list */
#include <stdio.h>
#include <stdlib.h>
#include "list.h"

s_node * list_create(void)
{
    return NULL;
}

void * list_get_data(s_node * node)
{
    return node->data;
}

void list_set_data(s_node * node, void * data)
{
    node->data = data;
}

s_node * list_insert(s_node * head, void * data)
{
    s_node * node = (s_node *) malloc(sizeof(s_node));
    list_set_data(node, data);
    node->next = head;
    return node;
}

s_node * list_append(s_node * head, void * data)
{
    if (!head) return list_insert(head, data);

    s_node * node = head;

    while (node->next) {
        node = node->next;
    }

    node->next = (s_node *) malloc(sizeof(s_node));
    node->next->next = list_create();
    list_set_data(node->next, data);

    return head;
}

int list_process(s_node * head, int (*fct)(s_node * node, void * param),
    void * param, s_node ** last)
{
    if (!head) return 0;

    s_node * node;
    for (node = head; node; node = node->next) {
        if (fct(node, param) == 1) {
            *last = node;
            return 1;
        }
    }
    return 0;
}

s_node * list_ordered_append(s_node ** head, int (*fct)(s_node * node, void * param),
    void * param)
{
    // quand elle est vide
    if (!(*head)) {
        *head = list_insert(*head, param);
        return *head;
    }

    // insertion en tete
    s_node * node;
    if (fct(*head, param) == 1) {
        *head = list_insert(*head, param);
        return (*head);
    }

    // cas general
    int res;
    for(node = *head; node->next; node = node->next) {
        if ((res = fct(node->next, param)) == 1) {
            node->next = list_insert(node->next, param);
            return node->next;
        } else if (res == 0) {
            return node->next;
        }
    }
    if (fct(node, param) == 0) return node;

    *head = list_append(*head, param);
    return node->next;
}

s_node * list_remove (s_node * head, void * data)
{
    if (!head) return head;

    for (s_node * node = head; node->next; node = node->next) {
        if (node->next->data == data) {
            s_node * n = node->next->next;
            free(node->next);
            node->next = n;
            break;
        }
    }

    return head;
}

s_node * list_headRemove(s_node * head)
{
    if (!head) return head;
    s_node * n = head->next;
    free(head);
    return n;
}

void * list_destroy(s_node * head)
{
     while (head)
        head = list_headRemove(head);
}

void afficher_s_node(s_node * list)
{
    printf("\nliste = [");
    while (list) {
        printf("%d,", *((int *)(list->data)));
        list = list->next;
    }
    printf("]\n");
    return;
}

int list_is_empty( s_node * node ) {
  return NULL == node;
}

unsigned int list_size(s_node * node)
{
    unsigned int i = 0;
    while (node) {
        node = node->next;
        i++;
    }
    return i;
}

/*PLUS
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
    void * param, s_node ** last)
{
    if (!head) return 0;

    s_node * node;
    for (node = head; node; node = node->next) {
        if (fct(node, param) == 1) {
            *last = node;
            return 1;
        }
    }
    return 0;
}
*/
/* hash table header */
#ifndef HACHAGE_H
#define HACHAGE_H

#include "list.h"

typedef struct {
    s_node * node;
    unsigned int len;
} super_list;

typedef struct {
    super_list * list;
    unsigned int len;
} strhash_table;

strhash_table * strhash_table_init(const unsigned int len);
strhash_table * strhash_table_destroy(strhash_table * table);
strhash_table * strhash_table_free(strhash_table * table);
char * strhash_table_add(strhash_table * table, char * str);
strhash_table * strhash_table_remove(strhash_table * table, char * str);
void strhash_table_info(strhash_table * table);
void strhash_print(strhash_table * table);

#endif


/****  c file hash table */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "hachage.h"
#include "list.h"

int hashCode(char * str, const int size_hash_table)
{
     int i, cle = 0;
     for (i = 0; str[i] != '\0'; i++) {
        cle *= 2;
        cle += (int) str[i];
    }
     return cle % size_hash_table;
}

int compare_str_add(s_node *node, void *param)
{
    int res = strcmp((char *) node->data, (char *) param);
    /*if the first non-matching character in node->data 
      is lower (in ASCII) than that of param.*/
    if (res < 0) return -1;
    
    /*if the first non-matching character in node->data 
      is greater (in ASCII) than that of param.*/
    if (res > 0) return 1;
    return 0;//if strings are equal
}


strhash_table * strhash_table_init(const unsigned int len)
{
    super_list *list = (super_list *) malloc(sizeof(super_list) * len);
    strhash_table * table = (strhash_table *) malloc(sizeof(strhash_table));

    if(!table) return NULL;
    table->len = len;
    if (!list) return NULL;

    for (unsigned int i = 0; i < len; i++) {
        list[i].node = list_create();
        list[i].len = 0;
    }
    table->list = list;

    return table;
}

strhash_table * strhash_table_destroy(strhash_table * table)
{
     unsigned int i;
    super_list *list;
    s_node *node, *next;

    for (i = 0; i < table->len; i++) {  
        list = table->list + i;
        node = list->node;
        while (node) {
            next = node->next;
            free(node->data);           
            free(node);
            node = next;
        }
    }
    free(table->list);
    free(table);
    return table; 
 
}

strhash_table * strhash_table_free(strhash_table * table)
{
    unsigned int i;
    super_list *list;
    for (i = 0; i < table->len; i++) {
        list = table->list + i;
        if (list->len > 0) {
            free(list->node->data);
            list_destroy(list->node);
            list->len = 0;
            return table;
        }
    }
    return table;
}

char * strhash_table_add(strhash_table * table, char * str)
{
    char * to_insert = strdup(str);
    int index = hashCode(str, table->len);

    s_node *ordered_Add = list_ordered_append(&(table->list[index].node), compare_str_add, to_insert);
    if (ordered_Add->data == to_insert)
        table->list[index].len++;
    else
        free(to_insert);
    return (char *) ordered_Add->data;
}

int find_str_node(s_node *node, void *param)
{
    return strcmp((char *) node->data, (char *) param) == 0 ? 1 : 0;
}

strhash_table * strhash_table_remove(strhash_table * table, char * str)
{
    const int index = hashCode(str, table->len);
    if (table->list[index].len == 0) return table;

    s_node *find_node;
    const int result = list_process(table->list[index].node, &find_str_node, str, &find_node);
    if (result == 1) {
        free(find_node->data);
        table->list[index].node = list_remove(table->list[index].node, find_node->data);
        table->list[index].len--;
    }
    return table;
}

void strhash_table_info(strhash_table * table)
{
    unsigned int i;
    unsigned int len, min, max;
    float deviation, moy;

    len = max = min = table->list[0].len;
    for (i = 1; i < table->len; i++) {
        if (table->list[i].len > max) max = table->list[i].len;
        else if (table->list[i].len < min) min = table->list[i].len;
        len += table->list[i].len;
    }

    moy = (float)len / table->len;
    deviation = 0;
    for (i = 0; i < table->len; i++) {
        deviation += (table->list[i].len - moy) * (table->list[i].len - moy);
    }
    deviation = (float) sqrt(deviation / table->len);

    printf("Table hachage : ");
    printf("%p\n",table);
    printf("\tNombre total d'élément : " );
    printf("%u\n",len );
    printf("\tNombre minimum : ");
    printf("%u\n", min);
    printf("\tNombre maximum : ");
    printf("%u\n", max );
    printf("\tÉcart type du nombre d'éléments par entrée : ");
    printf("%.2f\n", deviation );
    return;
}

void strhash_print(strhash_table * table)
{
    unsigned int i, j;
    super_list *list;
    s_node *node;
    printf("\nHash table de %p\n", table);
    printf("Start\n");
    for (i = 0; i < table->len; i++) {
        list = table->list + i;
        printf("\t%d ---\n", i);
        /****/
        for (j = 0, node = list->node; j < list->len; j++, node = node->next) 
        {
            printf( "\t\t%d. %s\n", j, (char *) node->data);
        }
    }
    printf("End\n");
    return;
}
/**** test file */

strhash_table * test_init(const unsigned int len)
{
    strhash_table * table = strhash_table_init(len);
    if (!table) {
        printf("Tha HashTable hasn't been created\n");
        assert(0);
    }
    printf("***Tha HashTable has been created***\n");
    return table;
}

strhash_table * test_destroy(strhash_table * table){
    
    table = strhash_table_destroy(table);
    if (table->list->node) {
        printf("The HashTable hasn't been destroyed (%p)\n", table->list->node);
        assert(0);
    }
    printf("The HashTable has been destroyed\n");
    return NULL;
}

int main(void)
{
    strhash_table * table =strhash_table_init(10);
    strhash_print(table);
    strhash_table_add(table, "ele1");
    strhash_table_add(table, "ele2");
    strhash_table_add(table, "ele3");
    strhash_table_add(table, "ele4");
    strhash_table_add(table, "ele5");
    //strhash_table_remove(table,"ele1");
    //strhash_table_free(table);
    test_destroy(table); 

    return 0;
}

提前谢谢你^^

【问题讨论】:

  • list_destroy函数中,当循环结束时会因为node是一个空指针。然后你在那个空指针上调用free。它是已定义的行为和无操作,这很好,但您仍然应该删除那个相当无用的调用。
  • 如果您的编译器支持,我还建议您使用地址和 UB 清理程序。或者像 Valgrind 或类似的内存调试器。
  • 我明白了,我会努力去做的,谢谢你的评论。

标签: c linked-list hashtable singly-linked-list


【解决方案1】:

您应该将list_destroy(list-&gt;node); 移到内部循环之外。您在循环中多次释放节点列表,您在循环中迭代节点链接。

这是修改后的版本:

strhash_table *strhash_table_destroy(strhash_table *table) {
    unsigned int i;
    super_list *list;
    s_node *node;

    for (i = 0; i < table->len; i++) {
        list = table->list + i;
        for (node = list->node; node != NULL; node = node->next) {
            free(node->data);
        }
        list_destroy(list->node);
    }
    free(table->list);
    free(table);
    return table;
}

void list_destroy(s_node *head) {
    while (head) {
        head = list_headRemove(head);
    }
}

s_node *list_headRemove(s_node *head) {
    if (!head) return head;
    s_node *n = head->next;
    free(head);
    return n;
}

更新

在发布的代码中,函数list_destroylist_headRemove 的版本存在冲突,此外,在第二个函数list_destroy 中有两次对free(node); 的调用,这两个函数都没有用,因为node 是一个while 循环退出时的空指针。

更新 2

strhash_table_free 中存在问题:您释放了 list-&gt;node,但您没有更新 list-&gt;node,因此该列表在 strhash_table_destroy 中被第二次释放,其中 list-&gt;len 未经过测试。

super_list 中的字段 len 似乎是多余的。您应该只测试node 成员是否为NULL,并在释放列表时将其设置为NULL

更新 3

最后的帖子几乎没有,也没有显示问题……但我发现了一些问题:

  • strhash_table_destroy 释放后返回table:这很糟糕,因为table 现在是无效指针。 strhash_table_destroy 不应返回任何内容。

  • test_destroystrhash_table_destroy 释放它之后解除对table 的引用。这具有未定义的行为。去掉这个测试函数,直接从main()调用strhash_table_destroy即可。

  • 在将strhash_table_remove 中的node-&gt;data 传递给list_remove 之前,你释放了node-&gt;data,这很糟糕,因为node-&gt;data 已失效。

  • 更糟糕的是:list_remove() 不测试是否应该删除 head 节点。在哈希表中具有单个元素的最小测试用例中,"ele1" 是头节点,因此该节点保留在列表中,len 递减并变得不同步,并且该节点具有无效的data strhash_table_destroy 的指针将尝试释放,导致双重释放问题。

  • strhash_table_free 似乎不正确且与strhash_table_destroy 不一致。

代码中可能还有其他问题。

这是一个经过一些简化和修复的修改版本:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

/**List header */
#ifndef LISTE_H
#define LISTE_H

struct _list_node {
    void *data;
    struct _list_node *next;
};

typedef struct _list_node s_node;

s_node *list_create(void);
void *list_get_data(s_node *node);
void list_set_data(s_node *node, void *data);
s_node *list_insert(s_node *head, void *data);
s_node *list_append(s_node *head, void *data);
s_node *list_process(s_node *head, int (*fct)(s_node *node, void *param), void *param);
s_node *list_ordered_append(s_node **head, int (*fct)(s_node *node, void *param), void *param);
s_node *list_headRemove(s_node *head);
s_node *list_remove(s_node *head, void *data);
void list_destroy(s_node *head);
void afficher_s_node(s_node *list);
int list_is_empty( s_node *node );
unsigned int list_size(s_node *node);
#endif

/****  c file list */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
//#include "list.h"

s_node *list_create(void) {
    return NULL;
}

void *list_get_data(s_node *node) {
    return node->data;
}

void list_set_data(s_node *node, void *data) {
    node->data = data;
}

s_node *list_insert(s_node *head, void *data) {
    s_node *node = (s_node *)malloc(sizeof(s_node));
    if (node) {
        list_set_data(node, data);
        node->next = head;
        return node;
    } else {
        return head;
    }
}

s_node *list_append(s_node *head, void *data) {
    if (!head) return list_insert(head, data);

    s_node *node = head;
    while (node->next) {
        node = node->next;
    }
    node->next = list_insert(NULL, data);

    return head;
}

s_node *list_process(s_node *head, int (*fct)(s_node *node, void *param), void *param) {
    for (s_node *node = head; node; node = node->next) {
        if (fct(node, param) == 1) {
            return node;
        }
    }
    return NULL;
}

s_node *list_ordered_append(s_node **head, int (*fct)(s_node *node, void *param), void *param) {
    s_node *node;
    int res;

    // empty list
    if (!*head) {
        return *head = list_insert(*head, param);
    }

    // insert at head
    res = fct(*head, param);
    if (res > 0) {
        return *head = list_insert(*head, param);
    }
    if (res == 0) {
        return *head;
    }

    // generic case
    for (node = *head; node->next; node = node->next) {
        res = fct(node->next, param);
        if (res > 1) {
            return node->next = list_insert(node->next, param);
        }
        if (res == 0) {
            return node->next;
        }
    }

    // append node
    return node->next = list_insert(NULL, param);
}

s_node *list_headRemove(s_node *head) {
    if (head) {
        s_node *n = head->next;
        free(head);
        return n;
    } else {
        return NULL;
    }
}

s_node *list_remove(s_node *head, void *data) {
    if (!head) return head;

    if (head->data == data) {
        head = list_headRemove(head);
    } else {
        for (s_node *node = head; node->next; node = node->next) {
            if (node->next->data == data) {
                node->next = list_headRemove(node->next);
                break;
            }
        }
    }
    return head;
}

void list_destroy(s_node *head) {
    while (head)
        head = list_headRemove(head);
}

int list_is_empty(s_node *node) {
    return node == NULL;
}

unsigned int list_size(s_node *node) {
    unsigned int i = 0;
    while (node) {
        node = node->next;
        i++;
    }
    return i;
}

/* hash table header */
#ifndef HACHAGE_H
#define HACHAGE_H

//#include "list.h"

typedef struct {
    s_node *node;
    unsigned int len;
} super_list;

typedef struct {
    super_list *list;
    unsigned int len;
} strhash_table;

strhash_table *strhash_table_init(const unsigned int len);
void strhash_table_destroy(strhash_table *table);
strhash_table *strhash_table_free(strhash_table *table);
char *strhash_table_add(strhash_table *table, const char *str);
int strhash_table_remove(strhash_table *table, const char *str);
void strhash_table_info(strhash_table *table);
void strhash_print(strhash_table *table);

#endif

/****  c file hash table */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//#include "hachage.h"
//#include "list.h"

int hashCode(const char *str, unsigned int size_hash_table) {
    unsigned int i, cle = 0;
    for (i = 0; str[i] != '\0'; i++) {
        cle *= 2;
        cle += (int)str[i];
    }
    return cle % size_hash_table;
}

int compare_str_add(s_node *node, void *param) {
    /* return <0 if node->data is before param, >0 if after, =0 if strings are equal */
    return strcmp((const char *)node->data, (const char *)param);
}

strhash_table *strhash_table_init(const unsigned int len) {
    strhash_table *table = (strhash_table *)malloc(sizeof(strhash_table));
    super_list *list = (super_list *)malloc(sizeof(super_list) * len);

    if (!table || !list) {
        free(table);
        free(list);
        return NULL;
    }

    for (unsigned int i = 0; i < len; i++) {
        list[i].node = list_create();
        list[i].len = 0;
    }
    table->list = list;
    table->len = len;

    return table;
}

void strhash_table_destroy(strhash_table *table) {
    for (unsigned int i = 0; i < table->len; i++) {
        super_list *list = table->list + i;
        s_node *node = list->node;
        while (node) {
            s_node *next = node->next;
            free(node->data);
            free(node);
            node = next;
        }
        list->node = NULL;
    }
    free(table->list);
    free(table);
}

char *strhash_table_add(strhash_table *table, const char *str) {
    char *to_insert = strdup(str);
    int index = hashCode(str, table->len);

    s_node *ordered_Add = list_ordered_append(&table->list[index].node, compare_str_add, to_insert);
    if (ordered_Add->data == to_insert) {
        /* node was inserted: increase len */
        table->list[index].len++;
    } else {
        /* node already present: free new data */
        free(to_insert);
    }
    return (char *)ordered_Add->data;
}

int find_str_node(s_node *node, void *param) {
    return strcmp((const char *)node->data, (const char *)param) == 0 ? 1 : 0;
}

// return 1 if successful
int strhash_table_remove(strhash_table *table, const char *str) {
    int index = hashCode(str, table->len);
    s_node *find_node = list_process(table->list[index].node, find_str_node, (void *)(uintptr_t)str);
    if (find_node) {
        /* node was found: free node and data */
        void *data = find_node->data;
        table->list[index].node = list_remove(table->list[index].node, data);
        table->list[index].len--;
        free(data);
        return 1;
    }
    return 0;
}

void strhash_table_info(strhash_table *table) {
    unsigned int i;
    unsigned int len, min, max;
    double deviation, moy;

    len = max = min = table->list[0].len;
    for (i = 1; i < table->len; i++) {
        if (table->list[i].len > max) max = table->list[i].len;
        else if (table->list[i].len < min) min = table->list[i].len;
        len += table->list[i].len;
    }

    moy = (double)len / table->len;
    deviation = 0;
    for (i = 0; i < table->len; i++) {
        deviation += (table->list[i].len - moy) * (table->list[i].len - moy);
    }
    deviation = sqrt(deviation / table->len);

    printf("Table hachage : ");
    printf("%p\n", (void *)table);
    printf("\tNombre total d'élément : " );
    printf("%u\n",len );
    printf("\tNombre minimum : ");
    printf("%u\n", min);
    printf("\tNombre maximum : ");
    printf("%u\n", max );
    printf("\tÉcart type du nombre d'éléments par entrée : ");
    printf("%.2f\n", deviation);
    return;
}

void strhash_print(strhash_table *table) {
    unsigned int i, j;
    super_list *list;
    s_node *node;
    printf("\nHash table de %p\n", (void *)table);
    printf("Start\n");
    for (i = 0; i < table->len; i++) {
        list = table->list + i;
        printf("\t%d ---\n", i);
        /****/
        for (j = 0, node = list->node; j < list->len; j++, node = node->next) {
            printf( "\t\t%d. %s\n", j, (char *)node->data);
        }
    }
    printf("End\n");
}

/**** test file */

int main(void) {
    strhash_table *table = strhash_table_init(10);
    strhash_print(table);
    strhash_table_add(table, "ele1");
    strhash_table_remove(table, "ele1");
    strhash_table_add(table, "ele1");
    strhash_table_add(table, "ele2");
    strhash_table_add(table, "ele3");
    strhash_table_add(table, "ele4");
    strhash_table_add(table, "ele5");
    strhash_table_remove(table, "ele1");
    strhash_print(table);
    strhash_table_destroy(table);

    return 0;
}

【讨论】:

  • 感谢您的评论,我之前确实这样做过,然后我重写了它,他们都给了我同样的问题。我的void list_destroy(s_node * head) 也有一个free(),我没有意识到它不见了
  • 我刚刚更新,我还有销售问题:(
  • 如果可以的话,我会在这里放更多代码
  • 我刚刚更新了帖子,最后的函数list_destroy是旧版本,即使我使用你提出的版本,我也遇到了同样的问题,所以问题来自list_destroy 使用 list_headRemove 并免费使用。我尝试使用@Scouarn 提出的另一个版本的strhash_table_destroy。我也遇到了同样的问题。
  • 我想到了这一点,在我的测试中,我根本没有使用strhash_table_free,我正在使用strhash_table_initstrhash_table_addstrhash_table_remove 和@987654371 @but 如果我想使用 strhash_table_free 它的工作。
【解决方案2】:

假设您不重复使用 list_headRemovelist_headRemove,我认为这是一种更清洁的方法:

strhash_table *strhash_table_destroy(strhash_table *table) {
    unsigned int i;
    super_list *list;
    s_node *node, *next;

    for (i = 0; i < table->len; i++) {
        
        list = table->list + i;
        node = list->node;

        while (node) {
            next = node->next;

            free(node->data);           
            free(node);

            node = next;
        }

    }

    free(table->list);
    free(table);

    return table; /* This pointer is not valid anymore be careful */
}

【讨论】:

  • 谢谢你的评论,我刚试过,我也有同样的问题,你认为这可能来自其他地方吗?
  • free(node-&gt;data) 吗?我认为应该编辑您的帖子以显示如何分配内容。
  • 我刚刚更新了
猜你喜欢
  • 2022-01-08
  • 2020-01-22
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多