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

C++双向链表头插后执行尾插触发未处理空指针异常问题

问题现象

C++实现双向链表时,单独执行头部插入、单独执行尾部插入逻辑均运行正常,但先执行头部插入再执行尾部插入时,会触发空指针访问错误,异常定位在InsertAtEnd函数内。

原实现代码如下:

#include <iostream>

using namespace std;

struct Node {
    int data;
    Node* prev;
    Node* next;
};

struct MyList {
    Node* head;
    Node* tail;
};

bool IsEmpty(MyList list) {
    if (list.head == nullptr) return true;
    else return false;
}

void Insert(MyList& list, int data) {
    Node* node = new Node();

    node->data = data;
    node->prev = nullptr;
    node->next = list.head;   //node->next points to NULL

    if (list.head == nullptr) {
        list.head = node;
        node->prev = nullptr;
    }
    else {
        //insert the new node at the beginning of the list
        list.head->prev = node;
        list.head = node;
        node->prev = nullptr;
    }
}

// Insert node at end of list
void InsertAtEnd(MyList& list, int data) {
    Node* node = new Node();

    node->data = data;
    node->next = nullptr;
    node->prev = nullptr;

    if (list.head == nullptr) { // Empty list
        list.head = node;
        list.tail = node;
    }
    else {
        list.tail->next = node;
        node->prev = list.tail;
        list.tail = node;
    }
}


//Traverse the list from the head
void PrintAll(const MyList& list) {
    Node* temp = list.head;

    if (temp == nullptr) {
        cout << "list is empty" << endl;
    }
    else {
        while (temp != nullptr)
        {
            cout << temp->data << endl;
            temp = temp->next;
        }

        cout << "*************************************" << endl;
    }
}

Node* Search(const MyList& list, int key) {
    Node* temp = list.head;

    while (temp != nullptr && temp->data != key)
    {
        temp = temp->next;
    }

    return temp;
}

void Delete(MyList& list, int key) {
    Node* temp = Search(list, key); //call search() 

    if (temp != nullptr)
    {
        if (temp->prev != nullptr)
        {
            temp->prev->next = temp->next;
        }
        else
        {
            list.head = temp->next;
        }

        if (temp->next != nullptr)
        {
            temp->next->prev = temp->prev;
        }
    }
}

int main() {

    MyList list;

    list.head = nullptr;    //initialize the linked-list
    list.tail = nullptr;

    if (IsEmpty(list)) 
        cout << "List is empty" << endl;



    Insert(list, 10);
    PrintAll(list);
    /*
    Insert(list, 20);

    Insert(list, 30);

    Insert(list, 40);
    */

    // Insert at end
    cout << "Now insert at end" << endl;
    InsertAtEnd(list, 70);

    InsertAtEnd(list, 45);

    InsertAtEnd(list, 59);

    InsertAtEnd(list, 12);

    InsertAtEnd(list, 33);
    PrintAll(list);
    

    /*
    int x = 24;
    Node* result = Search(list, x);

    if (result == nullptr) cout << "Cannot find " << x << endl;
    else cout << "Found " << result->data << endl;

    Delete(list, 200);
    PrintAll(list);

    Delete(list, 10);
    PrintAll(list);

    Delete(list, 40);
    PrintAll(list);

    Delete(list, 20);
    PrintAll(list);

    Delete(list, 30);
    PrintAll(list);
    */
    return 0;
}
根因分析

空指针错误的核心原因是链表头尾指针状态不一致:

  • 头部插入函数Insert在空链表中插入第一个节点时,只更新了head指针,完全没有同步设置tail指针。首次调用Insert(list,10)后,list.head指向值为10的节点,但list.tail仍然是初始化时的nullptr
  • 后续调用InsertAtEnd时,判断list.head != nullptr就进入非空分支,直接访问list.tail->next,此时list.tail是空指针,直接触发内存访问错误。
  • 原实现的Delete函数还存在两个隐藏问题:删除尾节点时没有更新tail指针、删除节点后没有释放内存,会在后续操作中触发同类崩溃和内存泄漏。
修复方案
  1. 修复头部插入函数,空链表插入首节点时同步更新tail指针:
void Insert(MyList& list, int data) {
    Node* node = new Node();

    node->data = data;
    node->prev = nullptr;
    node->next = list.head;

    if (list.head == nullptr) {
        list.head = node;
        list.tail = node; // 空链表插入首节点时同步设置tail
        node->prev = nullptr;
    }
    else {
        list.head->prev = node;
        list.head = node;
        node->prev = nullptr;
    }
}
  1. 修复删除函数,补充尾节点更新逻辑和内存释放:
void Delete(MyList& list, int key) {
    Node* temp = Search(list, key); 

    if (temp != nullptr)
    {
        if (temp->prev != nullptr)
        {
            temp->prev->next = temp->next;
        }
        else
        {
            list.head = temp->next;
        }

        if (temp->next != nullptr)
        {
            temp->next->prev = temp->prev;
        }
        else
        {
            // 删除的是尾节点时,更新tail指针
            list.tail = temp->prev;
        }
        delete temp; // 释放节点内存,避免泄漏
    }
}

修复后链表的head和tail指针在所有插入、删除场景下都能保持状态一致,不会再触发空指针错误。


内容的提问来源于stack exchange,提问作者Bill Moran

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 10:57:19