内存错误,不知道为什么会发生这种情况

时间:2013-12-30 17:12:27

标签: c memory-management memory-leaks valgrind

我有一些记忆错误,我无法理解它是什么以及它为什么会发生; 以下是源文件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)

这里到底发生了什么?!

1 个答案:

答案 0 :(得分:1)

我评论道:

  

请注意GetKey()中的代码为:

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

可能更简单:

POBJ p = (POBJ)e;
     

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

这些是陈腐的观察;准确,但不是直接问题的根源。

我注意到,我们在任何地方都没有pNode类型的定义(这是传递给e的{​​{1}}的类型)。还有许多其他类型缺失;因此,这不是一个SSCCE(Short, Self-Contained, Correct Example)。这主要意味着我们无法测试代码(没有做过多的工作),但这并不妨碍我们对代码进行一些观察。

我也评论道:

  

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

user2750466问道:

  

您能解释一下“分配到头部”是什么意思,您与哪一行有关?我理解的问题是head已被释放到某处,因此当我们调用head->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,有时是DelAux() - 根据TreeDelLeaf()的痕迹。

你有可能(似乎合理)传递valgrind(或ELEMENT **head - 它们是同一个东西,但我不想伪装指针,所以我个人不会创造或者使用PELEMENT *head typedef),这样你就可以写:

PELEMENT

它会影响调用代码中的变量。当然,函数中的其他地方也会有类似的变化 - 每个对*head = NULL; 的引用都需要进行审核,大多数需要由head替换,或者您要创建一个本地{{}除非您想要修改调用函数中的值,否则您将使用它代替*head

现在,行ELEMENT *l_head = *head;可能是head的递归调用 - 我们无法分辨,因为我们没有足够的代码(SSCCE)。如果这是对pStruct->delFunc(head->obj);的调用,那么根据修订的函数定义,将成为:

DelAux()

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

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

鉴于我没有工作代码(我不想看到你所有代码的副本;我希望...让我们说你的代码创建了100行SSCCE,但它需要是小的,或小的,都在一个文件中,因为它很小),我真的不愿意进一步说教。