【问题标题】:Program crashes when trying to delete last node of a linked list尝试删除链表的最后一个节点时程序崩溃
【发布时间】:2021-07-12 11:08:18
【问题描述】:

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

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

【问题讨论】:

  • OT:你应该对变量名node_numnode_number 做点什么。令人困惑。
  • 您将 curr_node->next 设置为 NULL,但您从未在循环中检查 curr_node 是否为 NULL。
  • @EmanuelP 对,我修好了
  • @EmanuelP 我添加了 if case if curr_node == NULL 这似乎是问题,但我不明白它是如何变成 NULL
  • curr_node->next = NULL; 后跟 curr_node = curr_node->next,然后是下一次迭代。

标签: c pointers linked-list double-pointer


【解决方案1】:

counter == position - 1current->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。该函数可以返回一个布尔值或整数值,指示是否找到并删除了节点。调用者应该处理打印消息。

【讨论】:

  • 非常感谢您的精彩解释和提示!您的代码确实结构更好,更清晰。我只需要花几分钟就能完全理解它。
  • 如果需要澄清,请告诉我。最后别忘了mark an answer as accepted
  • 对不起,我的错。我什么都懂,但我永远不会想出这个主意。只是一个问题,在输入所需位置后添加一个while循环是否更好,它将循环直到用户输入有效的节点位置?类似while(position < 1 || position > node_number) { printf("Enter position between 1 and %d: ", node_number); scanf("%d", &position); }
  • 是的,这是可能的。但是,我会将该逻辑保留在函数之外,并在主程序中处理用户输入和输出。这样您就可以将“模型”和“视图”关注点分开(另请参阅Separation of concernMVC 和相关主题)。但是有一个问题:while 条件不处理node_number 为零的情况。
  • 我只是想问你为什么处理这个函数之外的 I/O 更好。谢谢你的资源!
【解决方案2】:

在 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。
    if 语句结果为 True,现在您实际上删除了最后一个节点:
    ()--------------(°)--------------NULL
    作为第二次迭代的最后一个操作,您执行分配curr_node = curr_node->next;,结果为cur_node = NULL。最后,您以以下状态结束第二次迭代:
    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)

【讨论】:

  • 感谢您的解释。在将next 指向curr_node 地址的指针更改为NULL 之后,我还没有看到我正在尝试更改curr_node 地址。我刚刚添加了prev_node 并修复了它,但我想我也可以找到更简单的修复方法。
猜你喜欢
  • 2016-06-11
  • 1970-01-01
  • 2013-03-25
  • 2022-01-15
  • 2014-05-22
  • 1970-01-01
  • 1970-01-01
  • 2019-06-08
相关资源
最近更新 更多