You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

双向链表删除算法异常修复及全节点删除实现(C/C++)

双向链表删除异常(返回255)的问题分析与修正方案

Hey there, let's break down why your doubly linked list delete function is throwing unhandled exceptions and returning error code 255, then fix it properly so you can delete any node—including the head.

主要错误原因

Let's go through the critical issues in your code one by one:

  • 栈内存与堆内存误用: 你的input函数返回的是栈上的局部变量n2,在main里你把这些栈节点赋值给n1、n2等变量。当调用delete(&n1)时,你尝试free()一块栈内存,这是非法操作,直接触发内存访问违规。只有用malloc/calloc分配的堆内存才能被free。
  • 删除函数的空指针引用: delete函数没有处理要删除的节点是头节点(n1->previous为NULL)或尾节点(n1->next为NULL)的情况。比如删除头节点时,n1->previous->next会试图访问空指针的成员,直接导致程序崩溃。
  • 未维护链表头指针: 代码没有持续跟踪链表的头节点引用。当删除头节点时,需要更新头指针指向新的头节点,否则会产生悬空引用。

修正后的C代码

这里是修复后的版本,能正确处理头、中、尾任意节点的删除,且内存使用完全合规:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct list {
    char name[30];
    char subject1[30];
    char subject2[30];
    struct list* next;
    struct list* previous;
};
typedef struct list node;

// 创建堆分配的节点,并插入到链表尾部
node* input(node* last_node) {
    node* new_node = (node*)malloc(sizeof(node));
    if (new_node == NULL) {
        printf("内存分配失败!\n");
        exit(1);
    }

    // 初始化节点的前后指针
    new_node->previous = last_node;
    new_node->next = NULL;
    if (last_node != NULL) {
        last_node->next = new_node;
    }

    // 输入节点数据
    printf("输入姓名: ");
    scanf("%s", new_node->name);
    printf("输入科目1: ");
    scanf("%s", new_node->subject1);
    printf("输入科目2: ");
    scanf("%s", new_node->subject2);

    return new_node;
}

// 删除指定节点,同时更新链表头指针(若删除的是头节点)
void delete_node(node** head_ref, node* n1) {
    // 如果要删除的节点为空,直接返回
    if (n1 == NULL) {
        printf("没有可删除的节点!\n");
        return;
    }

    // 如果删除的是头节点,更新头指针
    if (n1->previous == NULL) {
        *head_ref = n1->next;
    } else {
        // 非头节点,让前驱节点指向后继节点
        n1->previous->next = n1->next;
    }

    // 如果删除的不是尾节点,让后继节点指向前驱节点
    if (n1->next != NULL) {
        n1->next->previous = n1->previous;
    }

    printf("\n正在删除 %s....\n", n1->name);
    free(n1);
}

// 遍历打印链表(用于验证操作结果)
void print_list(node* head) {
    node* current = head;
    printf("\n当前链表:\n");
    while (current != NULL) {
        printf("%s | %s | %s\n", current->name, current->subject1, current->subject2);
        current = current->next;
    }
}

int main() {
    node* head = NULL;

    // 插入节点:第一个节点作为链表头
    head = input(NULL);
    node* n1 = input(head);
    node* n2 = input(n1);

    // 打印初始链表
    print_list(head);

    // 测试删除:先删中间节点n1,再删尾节点n2,最后删头节点
    delete_node(&head, n1);
    print_list(head);

    delete_node(&head, n2);
    print_list(head);

    delete_node(&head, head);
    print_list(head);

    return 0;
}

关键改动说明

  • 仅使用堆内存: input函数现在返回堆分配节点的指针(通过malloc),所有节点都在动态内存中,彻底避免了栈内存误用的问题。
  • 安全的删除逻辑: delete_node函数接收头指针的双重指针(node** head_ref),这样在删除头节点时可以直接更新头指针。它会先检查空指针,避免非法引用。
  • 边界场景处理:
    • 删除头节点时:自动将头指针更新为下一个节点。
    • 删除尾节点时:跳过对不存在的后继节点的previous指针更新。
    • 检查要删除的节点是否为空,避免无效操作。
  • 辅助打印函数: 添加了print_list函数,方便验证删除前后的链表状态,调试更直观。

内容的提问来源于stack exchange,提问作者Debasrito Lahiri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:13:46