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

时间:2016-12-09 14:28:54

标签: c loops linked-list infinite

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

  1. 如果输入中的值为负

  2. ,则删除绝对值
  3. 如果数字为正数且均匀,则在列表顶部添加

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

  5. 如果数字等于零,请结束程序并打印列表。

  6. 我的问题是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
    

    任何线索?

3 个答案:

答案 0 :(得分:1)

有几件事,

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;
    }   
}

答案 1 :(得分:0)

正如@Vitek所指出的,修复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()的结果。 Opinions differ on whether or not you should do so,但没有演员的写作简化了代码。此外,您应该检查以确保malloc()已成功分配所请求的内存。如果您使用realloc(),这一点尤其重要,因为如果不这样做会导致内存泄漏。在此处创建新尾节点后,会立即设置vnext字段。

通过查看current节点而不是查看current->next节点来迭代链表通常似乎更简单。使用prev节点指针有助于实现此目的。新函数在列表上迭代,直到current == NULL,此时prev指向列表的最后一个节点。如果head作为NULL指针传入(即列表为空,如果用户输入的第一个数字为奇数,则会发生这种情况),跳过迭代循环,因为{ {1}}已初始化为current。循环后的代码将最后一个节点的head字段设置为指向新创建的尾节点,如果为该函数提供了非空列表,否则next成为新的尾节点。最后,head返回到调用函数。

可以对head函数和许多多余的代码应用很多简化。这个功能应该完全重新设计。你在哪里:

pop_el()

previous->next = current ->next; free(current); current = current->next; 引用的内存为free,然后尝试取消引用指向此内存的指针。致电current后,您不再拥有此内存,而这是undefined behavior。相反,你想要:

free()

但这并不重要,因为current = previous->next; 接下来设置为found,循环终止,而函数1 s return,不再使用backup。您应该将上述作业删除至current,因为不需要。

您应该能够使用此信息来改进程序中的其余代码。您可能还需要查看其他问题。 current指针通常是一种不好的做法 - 这只会使代码混淆并增加编程错误的可能性。您提供的规范并不清楚如果有多个节点包含相同的值会发生什么,即是否应删除一个或全部?并且,如果用户输入的第一个数字是typedef

,该怎么办?

答案 2 :(得分:-1)

首先,我建议在使用列表时使用递归函数。它更容易调试和理解。

我想我发现你的push_tail函数存在问题:

current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->v = v;

您为current-&gt; next分配内存,但将值分配给当前节点。 这应该是

current->next = (ll_node_ptr) malloc(sizeof(ll_node));
current->next->v = v;
current->next->next = NULL;

这可能与您的问题有关。