[英]Deleting a linked list element causes an infinite loop
我正在编写一个程序,它在输入中获取整数列表,并根据整数执行以下操作:
如果输入中的值为负,则删除绝对值
如果数字为正数且均匀,则在列表顶部添加
如果数字是正数和奇数,请将其添加到列表的尾部
如果该数字等于零,则结束程序并打印列表。
我的问题是pop_el函数,它导致列表上的无限循环,所以当我打印列表时,程序进入无限循环。 这是我的代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct ll_node_S * ll_node_ptr;
struct ll_node_S
{
int v;
ll_node_ptr next;
};
typedef struct ll_node_S ll_node;
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
while(current->next != NULL)
{
current = current->next;
}
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->v = v;
return backup;
}
ll_node_ptr push_head(ll_node_ptr head, int v)
{
ll_node_ptr new_head = (ll_node_ptr)malloc(sizeof(ll_node));
new_head->v = v;
new_head->next = head;
return new_head;
}
ll_node_ptr pop_el(ll_node_ptr head, int el)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
ll_node_ptr previous = NULL;
int found = 0;
while(current != NULL && !found)
{
if(current->v == el)
{
if(previous == NULL)
{
backup = current->next;
free(current);
current = backup;
previous = current;
}
else
{
previous->next = current ->next;
free(current);
current = current->next;
}
found = 1;
}
else
{
previous = current;
current = current->next;
}
}
return backup;
}
void print(ll_node_ptr head)
{
ll_node_ptr current = head;
printf("%d\n", head->v);
while(current->next != NULL)
{
current = current->next;
printf("%d\n", current->v);
}
}
int isPair(int n)
{
return ((n % 2) == 0);
}
int main(int argc, char** argv)
{
int n = 1;
ll_node_ptr list = NULL;
while(n != 0)
{
scanf("%d", &n);
if(n < 0)
{
list = pop_el(list, -n);
}
else
{
if(isPair(n))
{
list = push_head(list, n);
}
else
{
list = push_tail(list, n);
}
}
}
print(list);
//should free the list
return 0;
}
这是测试用例(在输入中传递)我正在测试代码:
4
5
2
-4
-5
-3
9
2
0
应产生以下输出:
2
2
9
任何线索?
好几件事,
在pop_el
,
1.如果previous
为NULL
则只需将head
ptr移动到下一个节点即可。 这样它就会成为新的head
。
if(previous == NULL)
{
backup = current->next;
free(current);
//current = backup; ---> Not needed.
//previous = current; ---> Not needed.
break; // ---> No need of setting found flag. You can remove it
}
2.如果之前不是NULL
则需要将上previous
节点下一个ptr指向current
节点的下一个节点。
else
{
previous->next = current ->next;
free(current);
//current = current->next; ---> Not needed.
break; // ---> No need of setting found flag. You can remove it
}
3.在push_tail
您为current->next
节点分配内存,在下一行中,您将v
添加到当前节点的v
。 那是错的。 检查以下,
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr backup = head;
ll_node_ptr current = head;
ll_node_ptr new = NULL; // Created new pointer
while(current->next != NULL)
{
current = current->next;
}
//current->next = (ll_node_ptr) malloc(sizeof(ll_node));
new = (ll_node_ptr) malloc(sizeof(ll_node));
//current->v = v; ----> incorrect. new Value is actually replacing the old value.
new->v = v; // New value is added in the newly created node.
new->next = NULL;
current->next = new;
return backup;
}
4.您可以改善您的print
逻辑
void print(ll_node_ptr head)
{
ll_node_ptr current = head;
//printf("%d\n", head->v);
while(current != NULL)
{
printf("%d\n", current->v);
current = current->next;
}
}
push_tail()
所指出的,通过修复push_tail()
函数可以解决您的直接问题。 原始代码不仅将值存储在错误的节点中,而且还无法将新分配的尾节点的next
字段设置为NULL
。 此函数还有一个问题: push_head()
和push_tail()
函数都需要在使用NULL
head
指针调用时创建并返回节点指针。 原始的push_head()
函数执行此操作,但push_tail()
函数不执行此操作。 如果用户输入的第一个数字是奇数,则第一个节点通过push_tail()
函数添加到列表中,导致未定义的行为(很可能是分段错误)。
更重要的是,您应该努力简化代码。 这将使编写更容易,也更容易调试。 例如, print()
函数可以减少为三行:
void print(ll_node_ptr current)
{
while(current) {
printf("%d\n", current->v);
current = current->next;
}
}
可以采取一些措施来改进push_tail()
函数。 这是一个新版本:
ll_node_ptr push_tail(ll_node_ptr head, int v)
{
ll_node_ptr current = head;
ll_node_ptr prev = NULL;
ll_node_ptr tail = malloc(sizeof(ll_node));
if (tail == NULL) {
fprintf(stderr, "Tail node allocation error\n");
exit(EXIT_FAILURE);
}
tail->v = v;
tail->next = NULL;
while (current) {
prev = current;
current = current->next;
}
if (head) {
prev->next = tail;
} else {
head = tail;
}
return head;
}
您不需要转换malloc()
的结果。 关于你是否应该这样做的意见不同 ,但是没有演员的写作简化了代码。 此外,您应该检查以确保malloc()
已成功分配所请求的内存。 如果您使用realloc()
,这一点尤其重要,因为如果不这样做会导致内存泄漏。 在此处创建新尾节点后,立即设置v
和next
字段。
通过查看current
节点而不是通过查看current->next
节点来迭代链接列表通常似乎更简单。 使用prev
节点指针有助于此。 新函数遍历列表,直到current == NULL
,此时prev
指向列表的最后一个节点。 如果head
作为NULL
指针传入(即,列表为空,如果用户输入的第一个数字是奇数,则会发生这种情况),跳过迭代循环,因为current
初始化为head
。 循环后的代码将最后一个节点的next
字段设置为指向新创建的尾节点,如果为该函数提供了非空列表,否则head
将成为新的尾节点。 最后, head
返回到调用函数。
有很多简化可以应用于pop_el()
函数和许多多余的代码。 这个功能应该完全重新设计。 你在哪里:
previous->next = current ->next;
free(current);
current = current->next;
你可以free
current
引用的内存,然后你尝试取消引用指向这个内存的指针。 在调用free()
,您不再拥有此内存,这是未定义的行为 。 相反,你想要:
current = previous->next;
但这并不重要,因为found
下一个设置为1
,循环终止,并且函数return
s backup
,不再使用current
。 您应该只删除上面的current
分配,因为它不需要。
您应该能够使用此信息来改进程序中的其余代码。 您可能还需要查看其他问题。 typedef
指针通常是一种不好的做法 - 这只会使代码混淆并增加编程错误的可能性。 如果有多个节点包含相同的值,即您应该删除一个或所有节点,那么您提供的规范并不清楚会发生什么? 而且,如果用户输入的第一个数字是0
,该怎么办?
首先,我建议在使用列表时使用递归函数。 它更容易调试和理解。
我想我发现你的push_tail函数存在问题:
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->v = v;
您为current-> next分配内存,但将值分配给当前节点。 这应该是
current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->next->v = v;
current->next->next = NULL;
也许这与你的问题有关。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.