尝试删除链表的最后一个节点时程序崩溃

Program crashes when trying to delete last node of a linked list

我正在创建一个程序,您可以在其中随时删除节点。但是,当我尝试删除列表中的最后一个节点时,程序崩溃了。有人可以告诉我我做错了什么吗?

void delete_node(Node **head_ref) {
    int position, counter = 0;
    Node *curr_node = *head_ref;

    if(*head_ref == NULL) {
        printf("No nodes to delete!");
        return;
    }

    printf("Enter position between 1 and %d: ", node_number);
    scanf("%d", &position);

    if(position == 1) {
        *head_ref = (*head_ref)->next;
        free(curr_node);
    } else if(position == node_number) {
        while(counter != position) {
            counter++;
            if(counter == position - 1)
                curr_node->next = NULL;
            curr_node = curr_node->next;
        }
    }

    printf("\nNode deleted successfully!\n");
    if( *head_ref == NULL )
        printf("\nLast node deleted!\n");
}

我正在调用 main 中的函数:

int main() {
    //... other part of the program
    delete_node(&head);
}

在 while 循环中,你做

while(counter != position) {
            counter++;

            if(counter == position - 1)
                curr_node->next = NULL;

            curr_node = curr_node->next;
        }

让我们以具有 3 个节点的列表为例:
我将使用 (°) 来标识 curr_node 指向的节点。

  • 第一次迭代:
    初始状态:
    (°)----------------()------------()
    计数器 = 0

您只需更改变量。

  • 第二次迭代:
    初始状态:
    ()------------(°)------------()
    计数器 = 1

    现在,您的代码做的第一件事是递增 counter。然后计数器的值现在是 2。
    如果语句结果为真,现在你实际上删除了最后一个节点:
    ()------------(°)------------空
    作为第二次迭代的最后一个操作,您执行了导致 cur_node = NULL 的赋值 curr_node = curr_node->next;。最后,您以以下状态结束第二次迭代:
    cur_node = NULL
    counter = 2

    问题是您将开始另一个迭代,因为结束条件是 counter != position,这仍然成立。所以你有第三次迭代:
  • 第三次迭代:
    初始状态:
    ()------------()-------------NULL°
    计数器 = 2
    注意:curr_node指向NULL。
    在此迭代结束时,您尝试执行 curr_node = curr_node->next;,这会导致崩溃,因为 curr_node 实际上是 NULL。

    您可以尝试通过更改 while 条件来解决 while (counter != position -1)

counter == position - 1你将current->next设置为NULL时,但是会再循环一次,在那个迭代中current会是NULL 所以当您在最后一次迭代中访问 current->next 时,您将 运行 进入异常。

您可以做的一些事情:

  • 引入一个 prev_node 引用,它在 curr_node 之后落后一步。
  • 您不需要将尾节点的删除作为一个单独的案例来处理。
  • 您不需要 counter,因为您可以减少 position 的值。
  • 不要忘记在 else 情况下释放内存,因为目前您只在删除头节点时这样做。
  • 删除成功后不要忘记减小node_number的值

这是一些更新的代码:

void delete_node(Node **head_ref) {
    int position;

    Node *curr_node = *head_ref;

    if(*head_ref == NULL) {
        printf("No nodes to delete!");
        return;
    }

    printf("Enter position between 1 and %d: ", node_number);
    scanf("%d", &position);

    if (position == 1) {
        *head_ref = (*head_ref)->next;
    } else { // All other positions can be treated in the same way
        Node *prev_node = curr_node; // Maintain a separate reference for previous node
        curr_node = curr_node->next;
        while (curr_node != NULL && position > 2) { // Can use position without counter
            prev_node = curr_node;
            curr_node = curr_node->next;
            position--;
        }
        if (curr_node == NULL) { // The input position was too great
            printf("\nThere is no node at that position!\n");
            return;
        }
        prev_node->next = curr_node->next;
    }
    free(curr_node);
    node_number--;  // Don't forget to keep this updated

    printf("\nNode deleted successfully!\n");
    if (*head_ref == NULL)
        printf("\nLast node deleted!\n");
}

注意:最好不要在这个函数中处理 I/O,而是将位置作为单独的参数传递。仅在主程序代码中处理 I/O。该函数可以 return 一个布尔值或整数,指示是否找到并删除了节点。调用方应处理打印消息。