【问题标题】:Segmentation fault when removing item from linked list从链表中删除项目时出现分段错误
【发布时间】:2014-08-16 12:04:04
【问题描述】:

我正在尝试创建程序,您可以在其中输入“+word”并添加单词,当您输入“-word”时,它会将单词从链接列表中取出。

插入单词对我来说很好,但删除它会导致分段错误。我不确定问题出在哪里。另外,有没有办法可以提示分段错误在哪里?

void
remove_from_list(struct linked_list *list, char *data)
{
    struct node *current_node = list->head;
    struct node *previous_node = NULL;

    while (current_node != NULL) {
        if (current_node->data == data) {
            break;
        }
        previous_node = current_node;
        current_node = current_node->next;
    }
    if (previous_node == NULL) {
        list->head = list->head->next;
    } else {
        previous_node->next = current_node->next;
    }
    free(current_node);
    if (list->tail == current_node)
        list->tail = previous_node;
}

int
main(void)
{
    struct linked_list list = { .head = NULL, .tail = NULL };
    char word[50];

    do {
        printf("Enter string: ");
        fgets(word, 50, stdin);
        if (word[0] == '+')
            add_to_list(&list, word);
        else if (word[0] == '-')
            remove_from_list(&list, word);
    } while (word[0] != '\n');

    print_list_rec(&list);
    free_list(&list);
    return 0;
}

【问题讨论】:

  • 您应该使用实际的字符串比较(如strncmp)来查找节点是否包含您的字符串。这一切:current_node->data == data 所做的只是比较指针。
  • 因为字符串在 C 中很棘手,如果仅从内存管理的角度来看,也许可以尝试使用相同的代码,但使用整数作为 data,看看是否遇到相同的问题。
  • 关于这一行:free_list(&list);,你不能释放堆栈上的结构。但是,您可以遍历链表,释放链表中的每个节点(第一个节点除外,因为它在堆栈上。
  • 函数 remove_from_list() 不能正确处理空列表,而是尝试 'list->head = list->head->next;'它正在获取地址 0 (null) 之后的偏移地址。
  • 关于这一行:struct linked_list list = { .head = NULL, .tail = NULL };不清除 .next 字段或 .data 字段。更好的一行是:struct linked_list list = { 0 };

标签: c linked-list segmentation-fault singly-linked-list


【解决方案1】:

您循环到链表的末尾,然后继续在此处取消引用 NULL 指针

} else {
    previous_node->next = current_node->next;
}

发生这种情况是因为您的比较实际上并未比较数据;

 if (current_node->data == data) {

你永远不会从那个 if 语句中得到真正的结果。

如果要比较字符串,请使用 strcmp()。

【讨论】:

    【解决方案2】:

    您遇到段错误的主要原因是您在尝试删除时没有处理列表中没有数据的情况。

    if (previous_node == NULL) { 
        list->head = list->head->next;
    } else { // ------------------------- If at the end of the list you go in here
        previous_node->next = current_node->next;
    }
    

    current_nodeNull 所以 current_node->next 段错误。

    您走到列表末尾的原因是您没有正确比较字符串的数据。使用strcmp() 像@this 建议正确比较。但是您应该处理列表中没有数据的情况。


    您可以在 while 循环和第一个 if 语句之间添加一个检查,这将处理一个空列表和不在列表中的数据 -

    if(current_node == NULL) // Empty list or wasn't found
        return;
    

    另一个说明:

    你先释放current_node,然后再检查它是否是尾巴。颠倒这个顺序。

    if (list->tail == current_node)
        list->tail = previous_node;
    free(current_node);
    

    【讨论】:

      【解决方案3】:

      在其他人说的之上,如果列表为空,则此代码将导致分段:

      if (previous_node == NULL) {
          list->head = list->head->next;
      }
      

      【讨论】:

        【解决方案4】:

        如果没有插入函数的代码,很难说它出了什么问题,因为删除的步骤很好。但是,插入可能有问题,因此这不起作用。

        但是您的代码中有一个问题,如果您尝试删除一个不存在的节点,您最终仍然会删除最后一个节点。您需要在 while 循环中断之前设置一个标志,然后仅在标志为 true 时删除该节点。

        【讨论】:

        • 注意:如果数据不在列表中,则不会删除最后一个节点 - 尝试访问 current_node->next 时会出现段错误。也不需要标志,只需要检查current_node 是否为Null
        【解决方案5】:

        函数应该如下所示

        void
        remove_from_list( struct linked_list *list, char *data )
        {
            struct node *current_node = list->head;
            struct node *previous_node = NULL;
        
            while ( current_node != NULL && strcmp( current_node->data, data ) != 0 ) 
            {
                previous_node = current_node;
                current_node = current_node->next;
            }
        
            if ( current_node != NULL ) 
            {
                if ( previous_node != NULL ) 
                    previous_node->next = current_node->next;
                else
                    head = head->next; 
        
                if ( list->tail == current_node )
                    list->tail = previous_node;
        
                free( current_node->data );
                free( current_node );
            }
        }
        

        我也会存储不带 + 或 - 的字符串。在这种情况下,main 中的 if 语句看起来像

            if ( word[0] == '+' )
                add_to_list( &list, word + 1 );
            else if ( word[0] == '-' )
                remove_from_list( &list, word + 1 );
        

        否则,您将永远找不到添加了加号的字符串以将其从列表中删除。

        【讨论】:

        • 应该使用 strcmp() 来比较字符串。
        • @Andrew_CS 哦,是的。:)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-08-15
        • 2017-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-10
        • 1970-01-01
        相关资源
        最近更新 更多