【问题标题】:C++ Segmentation Fault Linked List Element AdditionC++ 分段错误链表元素添加
【发布时间】:2014-08-12 12:38:02
【问题描述】:

我在用 c++ 编写链接列表实现时遇到了一个问题。每当我尝试添加元素时,我都会收到 Segmentation Fault 错误。

 #include <iostream>


    class node{
    public:
        int data;
        class node *next;
    };
    class LinkedList {
    public:
        node *head;
        LinkedList(int num);

        void add(int number);
        void display();
    };

    const int null = 0;
    LinkedList::LinkedList(int num) {
        // TODO Auto-generated constructor stub
        std::cout<<"Constructor Called"<<std::endl;
        head->data = num;
        head->next = null;
        std::cout<<"Constructor Call completed"<<std::endl;
    }



    void LinkedList::add(int num) {
        struct node *n=new node;
        n->data = num;
        n->next = null;
        struct node *current = head;
        while (current->next != null) {
            current = current->next;
        }
        current->next = n;
    }
    void LinkedList::display() {
        std::cout<<"Display"<<std::endl;
        struct node *current = head;

        while (current!= null) {
            std::cout << current->data << "->";
            current = current->next;
        }
        std::cout << "null"<<std::endl;
    }

    int main() {
        LinkedList l(1);
        l.display();
        l.add(2);
        l.display();
        return 0;
    }

请查看 gdb 调试日志:节点 n 没有引用任何内存位置。所以无法初始化。如果您需要任何进一步的信息,请告诉我。

提前致谢!

struct node *n=new node;
构造函数调用 构造函数调用完成 SampleCPP.cpp:60 处的断点 1,main () 60 l.display(); (gdb) 小号 SampleCPP.cpp:48 处的 LinkedList::display (this=0xbffff03c) 48 标准::cout (gdb) n 51同时(当前!=空){ (gdb) n 52 标准::cout 数据"; (gdb) n 53 当前=当前->下一个; (gdb) n 51同时(当前!=空){ (gdb) n 55 标准::cout 空 56 } (gdb) n SampleCPP.cpp:61 处的 main () 61 l.add(2); (gdb) 小号 LinkedList::add (this=0xbffff03c, num=2) at SampleCPP.cpp:38 38 结构节点 *n=新节点; (gdb) 打印 n $2 = (节点 *) 0x0 (gdb)

【问题讨论】:

    标签: c++ linked-list


    【解决方案1】:

    我想建议以下实现:

    #include <iostream>
    
    using namespace std;
    
    
    class node
    {
    private:
        int data;
        node* next;
    
    public:
        // Because of that we store the pointer, default implementations of the
        // copying operations are not acceptable -- they can lead to memory leakage.
        node(const node&) = delete;
        node& operator =(const node&) = delete;
    
        node(int d, node* prev = nullptr) :
            data(d), next(nullptr)
        {
            if(prev)
            {
                // Free memory before assigning the pointer.
                delete prev->next;
                prev->next = this;
            }
        }
        ~node()
        {
            // Deletes up to the end of the subchain.
            delete next;
        }
    
        inline node* getNext() const
        {
            return next;
        }
        inline operator int() const
        {
            return data;
        }
    };
    
    class LinkedList
    {
    private:
        node* head;
        node* curr;
    
    public:
        LinkedList(int first_entry) :
            head(new node(first_entry)), curr(head)
        {}
        ~LinkedList()
        {
            delete head;
        }
    
        void add(int entry)
        {
            curr = new node(entry, curr);
        }
        void display() const
        {
            for(node* i = head; i; i = i->getNext())
                cout << (int)*i << "->";
            cout << "null" << endl;
        }
    };
    
    
    int main(int argc, char* argv[])
    {
        LinkedList l(1);
        l.display();
        l.add(2);
        l.display();
    
        return EXIT_SUCCESS;
    }
    

    在这种方法中,需要额外注意内存管理。它也更密集地使用 OOP。

    【讨论】:

      【解决方案2】:

      稍作修改,

      class node{
      public:
          int data;
          class node *next;
          node(int num): data(num), next(NULL){} 
      };
      

      还有

      LinkedList::LinkedList(int num): head(new node(num))  {
          // TODO Auto-generated constructor stub
          std::cout<<"Constructor Called"<<std::endl;
          //Do sth if you wish
          std::cout<<"Constructor Call completed"<<std::endl;
      }
      

      这消除了一些额外的复杂性。更改使用新构造函数实例化节点的位置。

      【讨论】:

        【解决方案3】:

        你没有为 head 分配内存;应该是这样的

        LinkedList::LinkedList(int num) {
            // TODO Auto-generated constructor stub
            head=new node();
            std::cout<<"Constructor Called"<<std::endl;
            head->data = num;
            head->next = null;
            std::cout<<"Constructor Call completed"<<std::endl;
        }
        

        【讨论】:

        • 发布答案时,您至少应该先检查一下它是否没有添加任何内容。
        【解决方案4】:

        您永远不会为head 分配内存。那是你的问题。

        在构造函数中分配:

        LinkedList::LinkedList(int num) {
            std::cout<<"Constructor Called"<<std::endl;
            head = new node; // This is missing!
            head->data = num;
            head->next = null;
            std::cout<<"Constructor Call completed"<<std::endl;
        }
        

        在你的程序完成之前释放所有内存会很好..

        【讨论】:

        • 感谢您的帮助。它解决了我的目的。你能告诉我为什么构造函数本身没有发现问题吗?我使用不正确的代码调试了构造函数,但打印消息在 gdb 中正确打印了数据。
        • 我不确定,它可能需要额外的分析,但我可以提出一个假设:如果指针没有被初始化,它可能指向垃圾并写入它 可能不能使你的程序崩溃(它被称为未定义的行为,因为你不知道它会如何表现)
        • @Sambaran 我想您的意思是“为什么在构造函数本身中没有发现问题?”通过“为什么在编译时没有确定这点。否则该语句毫无意义。C++ 不会“强制”用户“应该”使用的任何内存管理模式,同时让他决定做什么和不做什么。如果你只是忘记了为某些指针变量分配内存(此处为头),编译器并不关心它(为什么要这样做?)。
        • 是的,Sargi 是对的。我想到了对你问题的解释,但我放弃了它,因为它看起来太“明显”了,但我并没有理所当然地认为:调试器并不意味着检查你的程序是否有未初始化的变量、未使用的代码和类似的东西(这些是静态分析)但它旨在帮助您找到错误(例如,在发生崩溃或其他情况下提供堆栈跟踪和变量监视)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多