我有一些记忆错误,我无法理解它是什么以及它为什么会发生; 以下是源文件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)
这里到底发生了什么?!
答案 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,但它需要是小的,或小的,都在一个文件中,因为它很小),我真的不愿意进一步说教。