【问题标题】:"free(): invalid pointer" error while freeing allocated memory of a hash table释放哈希表的分配内存时出现“free():无效指针”错误
【发布时间】:2018-08-05 08:46:06
【问题描述】:

编辑:

我已经通过 valgrind 运行了我的程序,它指向了我在这里包含的两个函数:

==6768== Invalid free() / delete / delete[] / realloc()
==6768==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6768==    by 0x42187C: free_memory (test4.c:232)
==6768==    by 0x42087E: main (test4.c:93)
==6768==  Address 0x10855f8 is 0 bytes inside data symbol "hash_table"
==6768== 
==6768== Invalid free() / delete / delete[] / realloc()
==6768==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6768==    by 0x421856: free_memory (test4.c:224)
==6768==    by 0x42087E: main (test4.c:93)
==6768==  Address 0x1085708 is 272 bytes inside data symbol "hash_table"
==6768== 
==6768== 
==6768== HEAP SUMMARY:
==6768==     in use at exit: 36,504 bytes in 676 blocks
==6768==   total heap usage: 679 allocs, 679 frees, 36,666 bytes allocated
==6768== 
==6768== 36,504 bytes in 676 blocks are definitely lost in loss record 1 of 1
==6768==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6768==    by 0x4208C9: init_table (test4.c:105)
==6768==    by 0x42073D: main (test4.c:44)
==6768== 
==6768== LEAK SUMMARY:
==6768==    definitely lost: 36,504 bytes in 676 blocks
==6768==    indirectly lost: 0 bytes in 0 blocks
==6768==      possibly lost: 0 bytes in 0 blocks
==6768==    still reachable: 0 bytes in 0 blocks
==6768==         suppressed: 0 bytes in 0 blocks
==6768== 
==6768== For counts of detected and suppressed errors, rerun with: -v
==6768== ERROR SUMMARY: 677 errors from 3 contexts (suppressed: 0 from 0)

大家好。如果你有时间,我有一个有趣的问题。几天来,我一直遇到来自stdlib.hfree() 函数的错误。我的代码有点大,所以我测试了它以在缩小版本中得到相同的错误。我知道问题出在哪里,但我无法制定解决方案。非常感谢所有帮助。完整的错误文本:

*** Error in `./sample': free(): invalid pointer: 0x0000000001084078 ***
Aborted

该代码包括一个数据结构,用于以第一个和第二个字母的字母索引作为键值来索引单词,并将单词本身存储在一个链表中,该链表由位于相应索引位置的节点头指向。这是一个非常简单的想法。正如一位伟人曾经说过的那样;谈话很便宜,给我看代码。开始阅读(1)到(3)的数据结构定义,会更有意义:

#include <stdlib.h>


// (3) Node (of a linked list), represents a single word indexed first letter of-
// column[i] and second letter of row[i]. Think of the z axis of a table

typedef struct _node
{
    char *value;
    struct _node *next;
}
node;


// (2) Row, represents the second letter of a word. Think of the y axis of a table

typedef struct _row
{
    node rows[26];
}
row;


// (1) Column, represents the first letter of a word. Think of the x axis of a table

typedef struct _column
{
    row columns[26];
}
column;


// These are detailed below

void init_table(column *table);
void free_memory(column *table);

column hash_table;


int main(void)
{

    init_table(&hash_table);
    free_memory(&hash_table);

    return 0;
}


// Initialize node-heads. If I don't do this, I get a 'runtime error: null-
// pointer passed as argument' when I want to access and store something in 
// "node->next" or "node->value" for the first time

void init_table(column *table)
{
    for (int i = 0; i < 26; i++)  // For each column
    {
        for (int j = 0; j < 26; j++)  // For each row
        {
            // Allocate space for a new node, as the head of a linked list.
            // Max word length will be 45 letters, allocate that much space
            node *first_node = malloc(sizeof(char) * 46 + sizeof(node *));

            // Assign new node with value of "none", and "next" (pointer) of NULL
            *first_node = (node) { .value = "none", .next = NULL };

            // Store this node in the current table cell
            table->columns[i].rows[j] = *first_node;
        }
    }
}


// Free all the memory on the heap, allocated previously
void free_memory(column *table)
{
    node *ptr;  // To guide the "del_node"
    node *del_node;  // To delete guided memory locations
    for (int i = 0; i < 26; i++)  // columns
    {
        for (int j = 0; j < 26; j++)  // rows
        {
            // Address of the first node of the current linked list
            ptr = &table->columns[i].rows[j];

            while(1)
            {
                if(ptr->next)  // If node does not point to "NULL"
                {
                    del_node = ptr;  // Guide the "del_node"
                    ptr = ptr->next;  // Advance "ptr" to the next node
                    free(del_node);  // Free the memory pointed by "del_node"
                }
                else {
                    break;
                }
            }

            // Free "ptr" in case there was no next node but "ptr" 
            // was pointing to a node

            if (ptr) free(ptr);
        }
    }
}

【问题讨论】:

  • 一个“列”是一个数组还是 26 个“列”键入“行”?
  • 在整个程序中使用valgrind
  • 顺便说一句:对于这么小的程序,您应该开始使用调试器来运行您的代码。至少它会告诉你错误发生在哪里。
  • 您对指针非常感到困惑。你尝试的一切基本上都被打破了。你需要先复习一下你的链表。编写在单个链表上工作的函数。调试它们。然后只需要一个 26×26 的链表数组。
  • 阅读how to debug small programs 和您使用的每个 function and header 的文档。使用所有警告和调试信息进行编译,因此 gcc -Wall -Wextra -g when 使用 GCC。改进您的代码以获得没有警告。使用调试器gdbvalgrind 了解程序的行为。另请参阅this answer

标签: c


【解决方案1】:

那段代码有很多问题。

你的错误可能来自这篇文章:

    for (int j = 0; j < 26; j++)  // rows
    {
        // Address of the first node of the current linked list
        ptr = &table->columns[i].rows[j];

记住:表是一个全局变量,不是动态分配的。 ptr 指向这个静态变量的数组元素。

        while(1)
        {
            if(ptr->next)  // If node does not point to "NULL"
            {
                del_node = ptr;  // Guide the "del_node"
                ptr = ptr->next;  // Advance "ptr" to the next node
                free(del_node);  // Free the memory pointed by "del_node"
            }

在这里,您尝试释放数组中的节点。不是动态分配的内存位置。那不会飞。

查看您初始化表的方式,我们发现了更多问题:

    for (int j = 0; j < 26; j++)  // For each row
    {
        // Allocate space for a new node, as the head of a linked list.
        // Max word length will be 45 letters, allocate that much space
        node *first_node = malloc(sizeof(char) * 46 + sizeof(node *));

您有 46 字节的内存无法使用 node 类型访问。

        // Assign new node with value of "none", and "next" (pointer) of NULL
        *first_node = (node) { .value = "none", .next = NULL };

虽然value 是一个指向char 的指针,但这可能有效。但是,如果您打算为其他节点使用动态分配的内存,则需要在之后释放它。这不适用于字符串文字。

        // Store this node in the current table cell
        table->columns[i].rows[j] = *first_node;

您复制了分配内存的内容(当然没有额外的 46 个字节),然后您忘记了分配的内存。你不能再释放它了。

如果这些节点都是列表的头部,则根本不应该为它们分配内存,因为它们是静态的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-09
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多