【问题标题】:VALGRIND invalid read of size 8, linked list, freeing nodesVALGRIND 无效读取大小为 8,链表,释放节点
【发布时间】:2015-09-03 16:27:33
【问题描述】:

我正在尝试找出 Valgrind 抱怨的原因。

如果有人能给我一个提示,以便我能理解为什么我的代码会产生不良行为,我将非常感激。

我创建了一个结构数组。每个条目都是由结构组成的链表的开头。现在我要释放该结构数组的每个链表的元素。

但是 Valgrind 说:

==15084== Memcheck, a memory error detector
==15084== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15084== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==15084== Command: /tmp/3RKi4ZeFa74f-a.out tests/16_allPositive output/ausgabe_16_allPositive
==15084== 
==15084== Invalid read of size 8
==15084==    at 0x402006: reset (1441261801.c:807)
==15084==    by 0x402489: main (1441261801.c:927)
==15084==  Address 0x51e0de8 is 8 bytes inside a block of size 16 free'd
==15084==    at 0x4C29E90: free (vg_replace_malloc.c:473)
==15084==    by 0x401FF5: reset (1441261801.c:809)
==15084==    by 0x402489: main (1441261801.c:927)
==15084== 
==15084== Invalid read of size 8
==15084==    at 0x401FEA: reset (1441261801.c:809)
==15084==    by 0x402489: main (1441261801.c:927)
==15084==  Address 0x51e0d48 is 8 bytes inside a block of size 16 free'd
==15084==    at 0x4C29E90: free (vg_replace_malloc.c:473)
==15084==    by 0x401FF5: reset (1441261801.c:809)
==15084==    by 0x402489: main (1441261801.c:927)
==15084== 
==15084== Invalid read of size 8
==15084==    at 0x401FFA: reset (1441261801.c:807)
==15084==    by 0x402489: main (1441261801.c:927)
==15084==  Address 0x51e0d48 is 8 bytes inside a block of size 16 free'd
==15084==    at 0x4C29E90: free (vg_replace_malloc.c:473)
==15084==    by 0x401FF5: reset (1441261801.c:809)
==15084==    by 0x402489: main (1441261801.c:927)
==15084== 
==15084== 
==15084== HEAP SUMMARY:
==15084==     in use at exit: 0 bytes in 0 blocks
==15084==   total heap usage: 119 allocs, 119 frees, 7,787 bytes allocated
==15084== 
==15084== All heap blocks were freed -- no leaks are possible
==15084== 
==15084== For counts of detected and suppressed errors, rerun with: -v
==15084== ERROR SUMMARY: 51 errors from 3 contexts (suppressed: 0 from 0)

这似乎是错误的功能

    void reset()
 784: {
 785:   //lösche alle (zeiger)char *arrays der conti structs
 786:   for(int i = 0; i < zeile1; i++)
 787:   {
 788:   struct node *p = &conti[i];
 789:   if((*p).next != NULL)
 790:   {
 791:       for(; (*p).next != NULL; p=(*p).next)
 792:       {
 793:                   free((*p).volume);
 794:       }
 795:                   free((*p).volume);
 796:   }else if ((*p).next == NULL)
 797:   {
 798:                   free((*p).volume);
 799:       }
 800:   }
 801:   //lösche die listenelemente der jeweiligen container
 802:   for(int i = 0; i < zeile1; i++)
 803:   {
 804:   struct node *p = &conti[i];
 805:   if((*p).next != NULL)
 806:   {
 807:       for(; (*p).next != NULL; p=(*p).next)
 808:       {
 809:                   free((*p).next);
 810:       }
 811:   }
 812:   }
 813:   //lösche die (zeiger)input char *arrays
 814:   for (int j = 0; j < zeile2; j++)
 815:   {
 816:       free(input[j].volume);
 817:       }
 818:   //lösche die struct arrays
 819:       free(conti);
 820:       free(input);
 821: }

结构如下所示:

  16: struct node {
  17:   char *volume;
  18:   struct node *next;
  19:   };

期待您的帮助。

【问题讨论】:

  • 你为什么写(*p).next而不是p-&gt;next?箭头运算符的发明是有充分理由的——请使用它。

标签: c pointers linked-list valgrind memcheck


【解决方案1】:

这是一种相当典型的编码风格,直到 (a) 您使用 valgrind 或 (b) 实际上有一个系统,当您释放它时另一个线程可以获取您的内存。

for(; (*p).next != NULL; p=(*p).next)
{
     free((*p).next);
}

当你通过那个循环时,你会抓住 p 然后你释放它指向的东西。

free(p->next); //p->next now points to freed memory

然后你得到你刚刚释放的东西

p = p->next; //p now points to freed memory

然后你树来释放指向的东西

free(p->next); //Trying to access freed memory

然后你必须有额外的代码来释放列表中的第一个元素,因为你不能在那个循环中释放它。

【讨论】:

  • 您好,感谢您的快速帮助。我没有想法。所以我理解我释放了两次东西。您提到了一种典型的编码风格。你所说的编码风格是什么意思?或者我怎样才能通过使用不同风格的 valgrind 来消除这个错误?
【解决方案2】:

请注意,Tom Tanner 在他的回答中给出了correct diagnosis。请把这归功于他。

请使用-&gt; 箭头运算符而不是(*p).next;发明它是有充分理由的。

您的释放代码当前是:

struct node *p = &conti[i];
if((*p).next != NULL)
{
    for(; (*p).next != NULL; p=(*p).next)
    {
        free((*p).next);
    }
}

请注意,循环会重复代码中if 条件中所做的测试;这是不必要的,您可以简单地删除 if 代码,但不能删除其中的循环。

代码应注意避免访问已释放的内存。您可以使用或多或少的标准技术来做到这一点,该技术在内存空闲之前保留 next 指针值:

struct node *p = &conti[i]->next;
while (p != NULL)
{
    struct node *next = p->next;
    free(p);
    p = next;
}

【讨论】:

  • 感谢大家的快速帮助!期待更多c编程!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 1970-01-01
  • 2016-09-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 2013-03-12
相关资源
最近更新 更多