首页 > 解决方案 > 链表错误:尝试访问“NULL”或未初始化的内容

问题描述

我正在开发一个 c++ 标头程序,在该程序中我需要创建一个链表并在它被编程以测试它是否被正确编码后通过 gitlab 测试器运行它。所以我写了我的代码,它在 VS 和 Xcode 中运行得很好,当我在 gitlab 中测试它时它失败了。我被告知的是

“您的代码正在访问一些为 NULL 的内存。由于这是在您要删除节点的函数中,因此您在删除单个节点函数或节点函数的删除部分中做错了。您可能会在前进之前删除节点您指向下一个节点的指针,这意味着您释放了节点的内存,但您没有跟踪节点的下一个指针,因此当您尝试推进指针或尝试编辑节点指向的位置时,您正在访问内存不应该。”

" 内存访问错误通常是在您尝试访问不存在的内容时。学生们的一个常见错误是插入函数中,当您尝试插入到您应该拥有的列表的后面时,如果语句检查这一点,因为插入的设置涉及到之后的节点,如果您在最后插入,则会发生内存访问冲突“

如果需要参考以及我编写的代码,这是一个包含程序要求的链接,我也会在此处包含它。但是我看不到我在哪里犯了错误以及如何解决它,如果有人可以提供帮助,那将非常有帮助。

https://docs.google.com/document/d/1Y4wFdhfl7Rhm3utLKQVrGH9xful819N-zlB-cTJARJw/edit?usp=sharing

List.hpp 代码:

#ifndef ECE275LIB_CONTAINERS_LIST_H
#define ECE275LIB_CONTAINERS_LIST_H
#include <stdio.h>
#include <iostream>

using namespace std;

namespace  ECE275Lib { namespace containers{
template <class type_name> class Node{
public:
    Node<type_name> *next;
    type_name data;
};

template <class type_name> class List{
public:
    Node<type_name> *head=new Node<type_name>;
    
    List(){
        head=NULL;
    }
    List(unsigned int N){
        head=NULL;
        for(unsigned int i=0;i<N;i++){
            push_back();
        }
    }
    
    void remove(type_name i){
        Node<type_name> *temp=new Node<type_name>;
        temp=head;
        Node<type_name> *temp2=new Node<type_name>;
        if(temp->data==i){
            head=temp->next;
            delete temp;
        }
        else{
            while(1){
                if(temp->next->data==i && temp->next->next==NULL){
                    temp2=temp->next;
                    delete temp2;
                    temp->next=NULL;
                    break;
                }
                
                else if(temp->next->data==i){
                    temp2=temp->next;
                    temp->next=temp2->next;
                    delete temp2;
                    break;
                }
                temp=temp->next;
            }
        }
    }
    
    void remove(Node<type_name> *node){
        Node<type_name> *temp;
        Node<type_name> *prev;
        temp=head;
        if(node==head){
            head=temp->next;
            delete temp;
            return ;
        }
        prev=head;
        while(1){
            temp=prev->next;
            if(node==temp){
                prev->next=temp->next;
                delete temp;
                break;
            }
            prev=prev->next;
        }
    }
    
    void remove(unsigned int s, unsigned int e){
        unsigned int i=0;
        Node<type_name> *temp=new Node<type_name>;
        Node<type_name> *nex=new Node<type_name>;
        Node<type_name> *curr=new Node<type_name>;
        temp=head;
        while(1){
            if(i==s){
                curr=temp;
                nex=temp->next;
                for(unsigned int j=i;j<=e;j++){
                    remove(curr);
                    curr=nex;
                    nex=nex->next;
                }
                break;
                
            }
            i++;
            temp=temp->next;
        }
    }
    
    void display(){
        
        Node<type_name> *temp=new Node<type_name>;
        temp=head;
        while(1){
            cout<<temp->data<<"\t";
            if(temp->next==NULL){
                break;
            }
            else{
                temp=temp->next;
            }
        }
    }
    
    void push_front(type_name d){
        Node<type_name> *temp=new Node<type_name>;
        if(head==NULL){
            temp->data=d;
            temp->next=NULL;
            head=temp;
        }
        else{
            temp->data=d;
            temp->next=head->next;
            head=temp;
        }
    }
    
    void push_back(type_name d){
        Node<type_name> *temp=new Node<type_name>;
        Node<type_name> *curr=new Node<type_name>;
        
        if(head==NULL){
            temp->data=d;
            temp->next=NULL;
            head=temp;
        }
        else{
            temp->data=d;
            temp->next=NULL;
            curr=head;
            while(1){
                if(curr->next==NULL){
                    curr->next=temp;
                    break;
                }
                curr=curr->next;
            }
        }
        
    }
    
    void insert(unsigned int index,type_name value){
        unsigned int i=0;
        
        Node<type_name> *temp=new Node<type_name>;
        Node<type_name> *temp2=new Node<type_name>;
        temp=head;
        if(index==0){
            push_front(value);
            
            return ;
        }
        while(1){
            if(i==index-1){
                temp2->data=value;
                temp2->next=temp->next;
                temp->next=temp2;
                break;
            }
            temp=temp->next;
            if(temp->next==NULL && index==i+2){
                Node<type_name> *temp3=new Node<type_name>;
                temp3->data=value;
                temp3->next=NULL;
                temp->next=temp3;
                break;
            }
            else if(temp->next==NULL){
                break;
            }
            i++;
        }
        
    }
    
    void push_back(){    // for empty initialization
        Node<type_name> *temp=new Node<type_name>;
        Node<type_name> *curr=new Node<type_name>;
        
        if(head==NULL){
            temp->next=NULL;
            head=temp;
        }
        else{
            temp->next=NULL;
            curr=head;
            while(1){
                if(curr->next==NULL){
                    curr->next=temp;
                    break;
                }
                curr=curr->next;
            }
        }
    }
    
    Node<type_name>* front(){
        return head;
    }
    
    Node<type_name>* back(){
        Node<type_name> *temp;
        temp=head;
        while(1){
            if(temp->next==NULL){
                return temp;
            }
            temp=temp->next;
        }
    }
    
    type_name at(unsigned int i){
        unsigned int j=0;
        Node<type_name> *temp;
        temp=head;
        while(1){
            if(j==i){
                return temp->data;
            }
            j++;
            temp=temp->next;
        }
    }
    
    void assign(unsigned int i, type_name d){
        unsigned int j=0;
        Node<type_name> *temp;
        temp=head;
        while(1){
            if(j==i){
                temp->data=d;
                break;
            }
            j++;
            temp=temp->next;
        }
    }
    
    unsigned int size(){
        unsigned int i=0;
        Node<type_name> *temp;
        temp=head;
        if(head==NULL){
            return 0;
        }
        while(1){
            if(temp->next==NULL){
                return i+1;
            }
            temp=temp->next;
            i++;
        }
    }
    
    void insert(unsigned int i,List<type_name> &other){
        unsigned int j=0;
        Node<type_name> *temp=new Node<type_name>;
        Node<type_name> *restore_point=new Node<type_name>;
        Node<type_name> *nodeOfOther=new Node<type_name>;
        temp=head;
        while(1){
            if(j==i-1){
                restore_point=temp->next;
                nodeOfOther=other.front();
                temp->next=nodeOfOther;
                other.back()->next=restore_point;
                break;
                
            }
            temp=temp->next;
            j++;
        }
    }
    
};
} }
using namespace ECE275Lib::containers;


#endif

标签: c++linked-list

解决方案


您的问题实际上是“调试我的代码”,最好在代码审查网站上提出,而不是在此处提出具体的、重点突出的问题。

但无论如何我都会给你一些建议,主要集中在删除逻辑上。由于您不是来称赞什么有效,我只会寻找并指出错误,这听起来很消极(对不起),但它是有帮助的。

  1. 永远不要 using namespace std在标题中(或任何其他命名空间,就此而言)。它污染了包含您的标头的任何人的全局命名空间,如果不选择退出您的所有代码,他们就无法退出它。

2A)看起来你在很多地方都调用“new”只是希望清除一个空指针,而不了解你为什么要点击一个。用一厢情愿的想法涂抹代码只会让你的代码变得更糟,如果你真的很不幸,它会隐藏错误症状,但你永远不会完全知道它是否已修复(但可能不是。)花时间去理解真正的问题在哪里。

2B)对于此代码,在您要向列表中添加新节点的位置最多应该调用一次。new无处。如果你使用智能指针和 make_unique,你可以把它降到零。

  1. remove() 永远不应该分配。绝不。见上文 2。

  2. 如果我调用 remove(3) 并且您的列表为空,会发生什么?Ans:在ifremove() 的第一个条件中, temp 将为 null 并且您将取消引用它。

  3. 您阅读 temp->next 而不检查它在几个地方是否为空。

  4. 阅读 temp->next->next 应该始终是错误的危险信号。即使它是有效的(可能不是),也应该尽可能避免。在链接列表中,如果您需要向前看那么远(如果有的话),您通常总是偏离轨道。你只是不知道下一个 null 在哪里。此外,您处于循环中,请记住您可以将一些工作保存到下一次迭代,而不是尝试在一次迭代中处理多个案例。

6b) 对于链表,查找头部通常不是一个好方法。最好留在当前节点上并跟踪您去过的地方(上一个)。不要或担心你要去哪里——下一次迭代会处理这个问题。如果您在需要删除的节点上,请将其删除,将 prev 的 next 修复到 current 的 next 并删除 current。

  1. 如果可以,请在声明变量时对其进行初始化。不要声明指针并使其未初始化,然后将下一行分配给它们。它有助于避免将来在分配变量之前从变量中读取的错误。

  2. 不要这样重复自己:

    if (temp->next->data==i && ...) ... 否则 if (temp->next->data==i) ...

最坏的结果是重复,但可能会引入错误和错失机会。

  1. 如果我要求 remove() 一个值,我想知道它是否真的被删除了。成功或失败的返回值使界面更好。

  2. temp对于变量来说,这是一个非常糟糕的名称,因为它没有给出数据的含义,而是对引用该数据的变量的生命周期进行了模糊的描述。当我在学校的时候,如果我们不使用有意义的名字,代码是不会被接受的,“避免诱惑”是戒律之一。事后看来,这仍然是个好建议。在这里,temp应该是“cur_node”或类似的有意义的东西。

祝你好运。


推荐阅读