繁体   English   中英

删除链接列表元素会导致无限循环

[英]Deleting a linked list element causes an infinite loop

我正在编写一个程序,它在输入中获取整数列表,并根据整数执行以下操作:

  1. 如果输入中的值为负,则删除绝对值

  2. 如果数字为正数且均匀,则在列表顶部添加

  3. 如果数字是正数和奇数,请将其添加到列表的尾部

  4. 如果该数字等于零,则结束程序并打印列表。

我的问题是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.如果previousNULL则只需将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() ,这一点尤其重要,因为如果不这样做会导致内存泄漏。 在此处创建新尾节点后,立即设置vnext字段。

通过查看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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM