【问题标题】:Memory errors, don't know why that is happening内存错误,不知道为什么会这样
【发布时间】:2013-12-30 17:12:26
【问题描述】:

我有一些记忆错误,我无法理解这是什么以及为什么会发生; 以下是来自源文件 gentree.c 的 typedef:

typedef struct _ELEMENT* PELEMENT; /* definition that PELEMENT is a pointer to  _ELEMENT*/
typedef struct _ELEMENT
{
  pNode obj; /*the id or content of the node*/
  PELEMENT* children; /* array of pointers*/
  PELEMENT parent; /*pointer*/
  int childrenCount; /* number of pointers at "children" array*/
} ELEMENT;

/* definition of the tree structure */
typedef struct _tree{
  PELEMENT head;
  int k;    /* the max number of children for each node*/
  GetKeyFunction getKeyFunc;
  CloneFunction cloneFunc;
  PrintFunction printFunc;
  DelFunction delFunc;

} Tree;

这是有问题的一段代码(来自源文件 gentree.c): 我从另一个函数调用这个函数,也可以在 gentree.c 中找到,但错误发生在这里:

Result DelAux(PELEMENT head, int key,PTree pStruct )
{
    PELEMENT saveParent;
int i=0, j=0;
    Result deleted=FAILURE; /*a variable to know if the leaf is successfully deleted*/
if(pStruct->getKeyFunc(head->obj)==key) /*if the current node is the wanted node*/
    {

    pStruct->delFunc(head->obj); /*first of all dlete the pointer to the node*/
    if(head->parent!=NULL) /*if it's not the root*/
    {
      (head->parent->childrenCount)--; /*decrease the number of the father's     children by one */
      if((head->parent->childrenCount)==0) /*if that was the last child of its father*/
      {
      saveParent=head->parent;
      free(head); /*free the pointer to the current node's element in tree*/
      head=NULL; /*and make it points to null*/
      free(saveParent->children); /*then free the pointer to childrens' array*/
      saveParent->children=NULL;  /*make the pointer to childrens' array points to null*/
      }
   }   
   else
   {
       free(head); /*free the pointer to the current node's element in tree*/
       head=NULL; /*and make it points to null*/
   }
        return SUCCESS;
   } 

   if (head->children==NULL)
   {
    return FAILURE;
   }
   else /*if the current node is not the wanted node*/
   {
       while(i<pStruct->k && j<head->childrenCount) /*then look for the wanted node it in the children of current node*/
       {
       if(head->children[i]!=NULL) /*if there is some child in this cell*/
       {
            j++;
                deleted=DelAux(head->children[i], key, pStruct);
                if(deleted==SUCCESS) /*if succeeded to delete*/
        {
                   return SUCCESS;
        }
           }
       i++;
       }

  }
  return FAILURE ; /*if didn't succeed */
}
/***********************************************************************/

以下是源文件 -partition.c 中的函数:

 typedef struct _OBJ* POBJ; 
 typedef struct _OBJ
 {
    double xi;
    double xf;
    double yi;
    double yf;
    int key;
 }
 PART;
/************************************************/
 int GetKey(pNode e)
{
POBJ p=NULL;
p=(POBJ)e;
return p->key; /*return the key of the node*/
}
/************************************************/
void Del(pNode e)
{
    POBJ p=NULL;
p= (POBJ)e; /*p pointe to the node we want to delete*/
free(p);    /*free the memory the node catches*/
p=NULL;    /*make it point to null*/
}
/************************************************/

这是 valgrind 的输出:

==24885== Memcheck, a memory error detector
==24885== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==24885== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==24885== Command: ./partition
==24885== Parent PID: 24884
==24885== 
--24885-- 
--24885-- Valgrind options:
--24885--    -v
--24885--    --log-file=log1
--24885--    --leak-check=full
--24885--    --tool=memcheck
--24885-- Contents of /proc/version:
--24885--   Linux version 2.6.32-358.14.1.el6.x86_64 (mockbuild@x86-     022.build.eng.bos.redhat.com) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-3) (GCC) ) #1 SMP    Mon Jun 17 15:54:20 EDT 2013
--24885-- Arch and hwcaps: AMD64, amd64-sse3-cx16-avx
--24885-- Page sizes: currently 4096, max supported 4096
--24885-- Valgrind library directory: /usr/lib64/valgrind
--24885-- Reading syms from /u1/004/syoavb/hm3/300028420/partition
--24885-- Reading syms from /usr/lib64/valgrind/memcheck-amd64-linux
--24885--    object doesn't have a dynamic symbol table
--24885-- Reading syms from /lib64/ld-2.12.so
--24885-- Scheduler: using generic scheduler lock implementation.
--24885-- Reading suppressions file: /usr/lib64/valgrind/default.supp
==24885== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-24885-by-syoavb-   on-t2.technion.ac.il
==24885== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-24885-by-syoavb-on-t2.technion.ac.il
==24885== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-24885-by-  syoavb-on-t2.technion.ac.il
==24885== 
==24885== TO CONTROL THIS PROCESS USING vgdb (which you probably
==24885== don't want to do, unless you know exactly what you're doing,
==24885== or are doing some strange experiment):
==24885==   /usr/lib64/valgrind/../../bin/vgdb --pid=24885 ...command...
==24885== 
==24885== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==24885==   /path/to/gdb ./partition
==24885== and then give GDB the following command
==24885==   target remote | /usr/lib64/valgrind/../../bin/vgdb --pid=24885
==24885== --pid is optional if only one valgrind process is running
==24885== 
--24885-- REDIR: 0x3fc8217520 (strlen) redirected to 0x38049551   (vgPlain_amd64_linux_REDIR_FOR_strlen)
--24885-- Reading syms from /usr/lib64/valgrind/vgpreload_core-amd64-linux.so
--24885-- Reading syms from /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so
--24885-- REDIR: 0x3fc8217390 (index) redirected to 0x4a07bf0 (index)
--24885-- REDIR: 0x3fc8217410 (strcmp) redirected to 0x4a08530 (strcmp)
--24885-- Reading syms from /lib64/libc-2.12.so
--24885-- REDIR: 0x3fc8684e80 (strcasecmp) redirected to 0x480155c   (_vgnU_ifunc_wrapper)
--24885-- REDIR: 0x3fc8687140 (strncasecmp) redirected to 0x480155c (_vgnU_ifunc_wrapper)
--24885-- REDIR: 0x3fc8682df0 (__GI_strrchr) redirected to 0x4a07a70 (__GI_strrchr)
--24885-- REDIR: 0x3fc867a8a0 (malloc) redirected to 0x4a0696c (malloc)
--24885-- REDIR: 0x3fc867a4e0 (calloc) redirected to 0x4a056d7 (calloc)
--24885-- REDIR: 0x3fc867b6d0 (free) redirected to 0x4a06369 (free)
--24885-- REDIR: 0x3fc8683500 (memchr) redirected to 0x4a085d0 (memchr)
--24885-- REDIR: 0x3fc8689820 (memcpy) redirected to 0x4a08b20 (memcpy)
--24885-- REDIR: 0x3fc8681310 (__GI_strlen) redirected to 0x4a07f70 (__GI_strlen)
--24885-- REDIR: 0x3fc868aca0 (strchrnul) redirected to 0x4a09f10 (strchrnul)
--24885-- REDIR: 0x3fc86846b0 (mempcpy) redirected to 0x4a09f80 (mempcpy)
==24885== Invalid read of size 4
==24885==    at 0x401FD0: GetKey (partition.c:342)
==24885==    by 0x40110F: DelAux (gentree.c:447)
==24885==    by 0x401213: DelAux (gentree.c:488)
==24885==    by 0x4010D1: TreeDelLeaf (gentree.c:433)
==24885==    by 0x4008C8: DestroyAux (gentree.c:115)
==24885==    by 0x400915: DestroyAux (gentree.c:124)
==24885==    by 0x400865: TreeDestroy (gentree.c:98)
==24885==    by 0x4019CF: DeletePartition (partition.c:177)
==24885==    by 0x402142: main (main.c:40)
==24885==  Address 0x4c23570 is 32 bytes inside a block of size 40 free'd
==24885==    at 0x4A063F0: free (vg_replace_malloc.c:446)
==24885==    by 0x401FA9: Del (partition.c:334)
==24885==    by 0x40112C: DelAux (gentree.c:455)
==24885==    by 0x401213: DelAux (gentree.c:488)
==24885==    by 0x4010D1: TreeDelLeaf (gentree.c:433)
==24885==    by 0x4008C8: DestroyAux (gentree.c:115)
==24885==    by 0x400915: DestroyAux (gentree.c:124)
==24885==    by 0x400865: TreeDestroy (gentree.c:98)
==24885==    by 0x4019CF: DeletePartition (partition.c:177)
==24885==    by 0x402142: main (main.c:40)
==24885== 
==24885== 
==24885== HEAP SUMMARY:
==24885==     in use at exit: 376 bytes in 11 blocks
==24885==   total heap usage: 98 allocs, 87 frees, 3,776 bytes allocated
==24885== 
==24885== Searching for pointers to 11 not-freed blocks
==24885== Checked 129,912 bytes
==24885== 
==24885== 376 (64 direct, 312 indirect) bytes in 2 blocks are definitely lost in loss record 3 of 3
==24885==    at 0x4A0577B: calloc (vg_replace_malloc.c:593)
==24885==    by 0x400C95: TreeAddLeaf (gentree.c:278)
==24885==    by 0x401942: RefineCell (partition.c:151)
==24885==    by 0x4020BB: main (main.c:28)
==24885== 
==24885== LEAK SUMMARY:
==24885==    definitely lost: 64 bytes in 2 blocks
==24885==    indirectly lost: 312 bytes in 9 blocks
==24885==      possibly lost: 0 bytes in 0 blocks
==24885==    still reachable: 0 bytes in 0 blocks
==24885==         suppressed: 0 bytes in 0 blocks
==24885== 
==24885== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)
==24885== 
==24885== 1 errors in context 1 of 2:
==24885== Invalid read of size 4
==24885==    at 0x401FD0: GetKey (partition.c:342)
==24885==    by 0x40110F: DelAux (gentree.c:447)
==24885==    by 0x401213: DelAux (gentree.c:488)
==24885==    by 0x4010D1: TreeDelLeaf (gentree.c:433)
==24885==    by 0x4008C8: DestroyAux (gentree.c:115)
==24885==    by 0x400915: DestroyAux (gentree.c:124)
==24885==    by 0x400865: TreeDestroy (gentree.c:98)
==24885==    by 0x4019CF: DeletePartition (partition.c:177)
==24885==    by 0x402142: main (main.c:40)
==24885==  Address 0x4c23570 is 32 bytes inside a block of size 40 free'd
==24885==    at 0x4A063F0: free (vg_replace_malloc.c:446)
==24885==    by 0x401FA9: Del (partition.c:334)
==24885==    by 0x40112C: DelAux (gentree.c:455)
==24885==    by 0x401213: DelAux (gentree.c:488)
==24885==    by 0x4010D1: TreeDelLeaf (gentree.c:433)
==24885==    by 0x4008C8: DestroyAux (gentree.c:115)
==24885==    by 0x400915: DestroyAux (gentree.c:124)
==24885==    by 0x400865: TreeDestroy (gentree.c:98)
==24885==    by 0x4019CF: DeletePartition (partition.c:177)
==24885==    by 0x402142: main (main.c:40)
==24885== 
--24885-- 
--24885-- used_suppression:      4 U1004-ARM-_dl_relocate_object
--24885-- used_suppression:      2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a
==24885== 
==24885== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)

这里到底发生了什么?!

【问题讨论】:

  • 这条信息似乎不言自明;你正试图从你已经释放的内存中读取。至于为什么会这样,你需要使用调试器来跟踪你的指针。
  • 请注意,GetKey() 中的代码如下:POBJ p=NULL; p=(POBJ)e; 可能更简单:POBJ p = (POBJ)e;(初始化为 NULL 然后立即分配是没有意义的)。同样,在Del 中,您不需要将 NULL 分配给即将超出范围的局部变量。在DelAux() 中,当您分配给head 时,您只影响局部变量(函数参数),而不是调用函数中的指针。这意味着您的调用函数可能没有意识到它正在访问的数据已被释放。
  • @Jonathan Leffler 你能解释一下“分配给头”是什么意思,你与哪一行有关?据我了解,问题是 head->obj 已在某处被释放,因此当我们调用 Getkey 时出现内存错误,请告诉我我是否错了。

标签: c memory-management memory-leaks valgrind


【解决方案1】:

我评论了:

请注意,GetKey() 中的代码如下:

POBJ p=NULL;
p=(POBJ)e;

可能更简单:

POBJ p = (POBJ)e;

(初始化为 NULL 然后立即 赋值)。同样,在Del 中,您不需要将 NULL 分配给 即将超出范围的局部变量。

这些都是陈词滥调;准确,但不是直接问题的根源。

顺便说一句,我们在任何地方都没有pNode 类型的定义(这是传递给GetKey()e 类型)。还有许多其他类型也丢失了;因此,这不是 SSCCE (Short, Self-Contained, Correct Example)。这主要意味着我们无法测试代码(不做不必要的工作),但这并不妨碍我们对代码进行一些观察。

我也评论了:

DelAux(),当你 分配给head,你只影响局部变量(函数 参数),而不是调用函数中的指针。这意味着 您的调用函数可能没有意识到它正在访问的数据 已经被释放了。

还有user2750466问:

您能否解释一下“分配给负责人”是什么意思,您与哪一行有关?据我了解,问题是head-&gt;obj 已在某处被释放,因此当我们调用Getkey() 时出现内存错误;如果我错了,请告诉我。

这将占用太多空间来作为评论明智,因此它成为一个答案。它是否解决了整个问题是一个单独的讨论。

我提到的代码在DelAux();相关的取消评论摘录是:

Result DelAux(PELEMENT head, int key, PTree pStruct)
{
    PELEMENT saveParent;
    int i = 0, j = 0;
    Result deleted = FAILURE;
    if (pStruct->getKeyFunc(head->obj) == key)
    {
        pStruct->delFunc(head->obj);
        if (head->parent != NULL)
        {
            (head->parent->childrenCount)--;
            if ((head->parent->childrenCount) == 0)
            {
                saveParent = head->parent;
                free(head);
                head = NULL;  // This is (1)
                free(saveParent->children);
                saveParent->children = NULL;
            }
        }
        else
        {
            free(head);
            head = NULL;  // This is (2)
        }
        return SUCCESS;
    }
    ...

head 变量作为值传递给此函数;它是一个指针,但它是DelAux() 函数的局部变量。我在代码中确定了将NULL 分配给head 的两个位置,这是没有意义的,因为您只影响局部变量head,而不是调用函数中的指针(有时是@ 987654341@,其他场合TreeDelLeaf()——根据valgrind的痕迹。

您可能需要传入ELEMENT **head(或PELEMENT *head——它们是相同的东西,但我不想掩饰指针,所以我个人不会创建或使用@ 987654346@ typedef),所以你可以这样写:

*head = NULL;

它会影响调用代码中的变量。当然,函数的其他地方也会有类似的变化——每个对 head 的引用都需要审查,并且大多数都需要替换为 *head,或者你可以创建一个本地 ELEMENT *l_head = *head; d 代替head,除非您想修改调用函数中的值。

现在,pStruct-&gt;delFunc(head-&gt;obj); 行可能是对DelAux() 的(该)递归调用——我们无法判断,因为我们没有足够的代码(又是 SSCCE)。如果这是对DelAux() 的调用,那么在修改后的函数定义下,它将变为:

pStruct->delFunc(&head->obj);

这会将指针的地址传递给函数,因此函数可以修改指针——特别是将其设置为 NULL——并且调用函数会知道。如目前所写,被调用函数将其head-&gt;obj 的副本设置为NULL,而不影响head-&gt;obj 本身。

这是我解释的评论的要点。这是学习C和构建树结构等的人的常见问题。关于SO,这里有很多类似的问题。

鉴于我没有工作代码(而且我不想看到您所有代码的副本;我想要……假设是从您的代码创建的 100 行 SSCCE,但它必须是小,或者更小,都在一个文件中,因为它太小了),我不太愿意进一步自夸。

【讨论】:

  • 由于您的答案已经包含很多内容,您可以添加他尝试访问的 pNode 的关键元素可能未分配,这就是为什么存在无效的大小为 4 的读取。此外,他正在尝试释放一个已在Del() 中释放的指针。
  • @FreeSalad:虽然这可能是问题所在,但尚不清楚是否是问题所在。 valgrind 报告指向使用已被释放的指针。此外,它是 40 字节分配中的 32 字节,所以我猜它是 32 位编译中 struct _ELEMENTparent 成员(因此 sizeof(pNode) 是 28。(我还应该观察_ELEMENT 这个名字是为实现而保留的,不应该被这个用户代码使用。)然而,这更像是一个猜测而不是一个确定的答案;我没有花时间去寻找数据的使用位置。跨度>
  • Jonathan Leffler,我认为你是对的,head 已被释放,但它的 NULL 分配并未完成,因为它是通过值而不是通过引用移动到函数的;因此,当函数到达这一行时:if(head->children[i]!=NULL),它通过“if 条件”,因为那里没有 NULL,并且在下一次递归调用中,当它到达 GetKey 时,它发送 head ->我们在之前的调用中释放的obj。
猜你喜欢
  • 2021-09-22
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
  • 2021-05-17
  • 1970-01-01
  • 2020-02-14
相关资源
最近更新 更多