【问题标题】:Linked list error: trying to access something that is 'NULL' or uninitialized链表错误:尝试访问“NULL”或未初始化的内容
【发布时间】:2020-12-06 02:18:07
【问题描述】:

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

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

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

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

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++ 中使用 nullptr 而不是 C 的无类型 NULL。另外不要忘记auto,它可以让您的生活更轻松。
  • 这里有大量代码需要查看,乍一看,方式超过了典型的链表所需的内容。为什么remove() 涉及任何分配?有很多东西被创造出来,有时似乎被随意删除。
  • 如果你的变量名能真正传达一些东西,那还不够,你有一两个评论就好了。这段代码远非不言自明。
  • Hello 内存泄漏:Node&lt;type_name&gt; *temp=new Node&lt;type_name&gt;; temp=head;remove 中还有三个。

标签: c++ linked-list


【解决方案1】:

您的问题实际上是“调试我的代码”,最好在代码审查网站上提问,而不是在此处提问,因为这里是针对特定的、重点突出的问题。

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

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

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

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

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

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

  3. 你读了 temp->next 却没有检查它在几个地方是否为空。

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

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

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

  2. 不要这样重复自己:

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

在最坏的情况下,您最终会出现重复,但可能会引入错误并错失机会。

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

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

祝你好运。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    • 2011-04-14
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多