我正在试图在c中创建一个链接列表,其中包含遵循此结构的各种节点:
typedef struct sll_node
{
char label[LABEL_SIZE];
int value;
struct sll_node* next;
}sll_node;
我的输出是我运行代码的每个测试的预期输出,但是当在valgrind下运行时,我收到内存泄漏错误;
==8001== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8001== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8001== by 0x4012B8: make_node (slist.c:53)
==8001== by 0x401337: sll_add (slist.c:91)
==8001== by 0x40108B: test_stress1 (sample-driver.c:188)
==8001== by 0x401277: main (sample-driver.c:258)
我已经检查了将新节点添加到列表中的所有代码,但我认为那里没有错误
sll_node *sll_add(sll_node *list, int value, const char *label)
{
sll_node *NewNode = make_node(value, label);
sll_node *ListNode = list;
if(list == 0)
return NewNode;
while(ListNode->next != 0)
{
ListNode = ListNode->next;
}
ListNode->next = NewNode;
return list;
}
所以我认为剩下的唯一错误是在从列表中删除节点的函数中。
该函数应该在所有节点中搜索具有应该擦除的值的节点,并且如果由于此操作而改变了头节点,则将新的开头返回到列表中,以防它找不到它应该只返回列表指针的搜索值:
sll_node *sll_remove(sll_node *list, int search_value)
{
sll_node *IwillDeleteThis = list;
sll_node *ThisIsBeforeWhatIWillDelete = 0;
if(list == 0)
return 0;
if(list->value == search_value)
{
if(list->next)
{
sll_node *NewBeginning = list->next;
free(list);
return NewBeginning;
}
free(list);
return 0;
}
while(IwillDeleteThis)
{
if(IwillDeleteThis->value == search_value)
{
ThisIsBeforeWhatIWillDelete->next = IwillDeleteThis->next;
free(IwillDeleteThis);
return list;
}
ThisIsBeforeWhatIWillDelete = IwillDeleteThis;
IwillDeleteThis = IwillDeleteThis->next;
}
return list;
}
我认为,在评估我必须删除的列表的可能性时,我可能会感到很沮丧,但我不知道它是什么情况。
如果你指出我错在哪里,那将是非常有帮助的。
谢谢。
修改
以防万一,这是我用来测试代码的代码:
#define VERBOSEx
void test_stress1(void)
{
#define SIZE 4500
sll_node *list = NULL;
int i, count = 0;
int *a = malloc(2 * SIZE * sizeof(int));
char buf[LABEL_SIZE];
for (i = 0; i < SIZE; i++)
{
a[count++] = i + 1;
sprintf(buf, "%08i", a[i]);
list = sll_add(list, a[i], buf);
}
#ifdef VERBOSE
sll_dump(list);
#endif
for (i = 0; i < SIZE; i++)
{
int r1 = RandomInt(0, 1);
int r2 = RandomInt(1, count);
a[count] = count + 1;
count++;
sprintf(buf, "%08i", count);
if (r1)
list = sll_insert_before(list, r2, count, buf);
else
sll_insert_after(list, r2, count, buf);
#ifdef VERBOSE
sll_dump(list);
printf("%s %i", r1 ? "before" : "after", r2);
#endif
}
#ifdef VERBOSE
sll_dump(list);
#endif
/*PrintArray(a, count);*/
Shuffle(a, count);
/*PrintArray(a, count);*/
for (i = 0; i < 2 * SIZE - 20; i++)
{
list = sll_remove(list, a[i]);
#ifdef VERBOSE
sll_dump(list);
#endif
}
sll_dump(list);
sll_destroy(list);
free(a);
}
EDIT2:
这是破坏列表的代码:
void sll_destroy(sll_node *list)
{
sll_node *NodeToDelete = list;
if(list == 0)
return;
while(NodeToDelete)
{
sll_node *DeleteNow = NodeToDelete;
NodeToDelete = NodeToDelete->next;
free(DeleteNow);
}
}
EDIT3: 我用来将节点添加到列表中的一些额外函数
sll_node *sll_insert_before(sll_node *list, int search_value, int value, const char *label)
{
sll_node *NewNode = make_node(value, label);
sll_node *NodeToChangePosWith = list;
sll_node *PreviousNode = list;
if(list->value != search_value)
{
while(NodeToChangePosWith != 0 &&
NodeToChangePosWith->value != search_value)
{
PreviousNode = NodeToChangePosWith;
NodeToChangePosWith = NodeToChangePosWith->next;
}
}
else
{
NewNode->next = list;
return NewNode;
}
if(NodeToChangePosWith == 0)
{
free(NewNode);
return list;
}
PreviousNode->next = NewNode;
NewNode->next = NodeToChangePosWith;
if(PreviousNode == list)
{
return NewNode;
}
return list;
}
void sll_insert_after(sll_node *list, int search_value, int value, const char *label)
{
sll_node *NewNode = make_node(value, label);
sll_node *NodeToChangePosWith = list;
while(NodeToChangePosWith->next != 0 &&
NodeToChangePosWith->value != search_value)
{
NodeToChangePosWith = NodeToChangePosWith->next;
}
if(NodeToChangePosWith->next == 0 &&
NodeToChangePosWith->value != search_value)
{
free(NewNode);
return;
}
NewNode->next = NodeToChangePosWith->next;
NodeToChangePosWith->next = NewNode;
}
答案 0 :(得分:1)
您在此处复制的源代码太多;但为了找到错误,我建议你做以下事情:
1-使用Valgrind的Massif heap profiler
2-使用Massif visualizer查看内存的分配和释放位置(此工具可视化最后一步的输出。
3-另外,为了查看程序执行期间调用的函数,我建议您使用Valgrind's Callgrind和KCachegrind来显示它的输出。
答案 1 :(得分:1)
当您在列表的第二个元素之前插入时,错误位于sll_insert_before
。在这种情况下,在while循环之后,PreviousNode
将指向第一个元素,NodeToChangePosWith
将指向第二个元素。您在NewNode
和PreviousNode
之间插入了NodeToChangePosWith
,但是您有一个不必要的(并且不正确的)测试,看看PreviousNode
是否等于list
(它是在这种情况下),并返回NewNode
作为新的第一个元素,它泄漏了真正的第一个元素。
顺便说一下,以下代码消除了所有特殊情况:
sll_node *sll_add(sll_node *list, int value, const char *label)
{
sll_node *newNode = make_node(value, label);
sll_node **nextPtr = &list;
while(*nextPtr != 0)
{
nextPtr = &(*nextPtr)->next;
}
*nextPtr = newNode;
return list;
}
sll_node *sll_remove(sll_node *list, int search_value)
{
sll_node **nextPtr;
sll_node *node;
for(nextPtr = &list; (node = *nextPtr) != 0; nextPtr = &node->next)
{
if(node->value == search_value)
{
*nextPtr = node->next;
free(node);
break;
}
}
return list;
}
void sll_destroy(sll_node *list)
{
while(list != 0)
{
sll_node *nodeToDelete = list;
list = list->next;
free(nodeToDelete);
}
}
sll_node *sll_insert_before(sll_node *list, int search_value, int value, const char *label)
{
sll_node **nextPtr;
sll_node *node;
for(nextPtr = &list; (node = *nextPtr) != 0; nextPtr = &node->next)
{
if(node->value == search_value)
{
sll_node *newNode = make_node(value, label);
newNode->next = node;
*nextPtr = newNode;
break;
}
}
return list;
}
void sll_insert_after(sll_node *list, int search_value, int value, const char *label)
{
while(list != 0)
{
if(list->value == search_value)
{
sll_node *newNode = make_node(value, label);
newNode->next = list->next;
list->next = newNode;
break;
}
}
}