如何修复链表中的分段错误

How to fix Segmentation Fault in linked list

我遇到了一个分段错误,我不知道如何修复。 List.cpp 和 List.hpp 更大,但我添加了我在 main.cpp 中使用的内容。这是代码:

List.hpp

#ifndef LIST_H
#define LIST_H

#include <iostream>
#include <cstdlib>

struct Node{
    int _value;
    Node *_next;
};

struct List{
    Node *_head;
    int _size;

    List();
    void insert(int value);
    void print();
};

#endif

List.cpp

#include "List.hpp"

List::List(){
    _size = 0;
    _head = nullptr;
}

void List::insert(int value){
    Node* node;
    node->_value = value;
    node->_next = _head;
    _head = node;
}

void List::print(){
    Node* head = _head;
    if (_size > 0){
        while(head){
            std::cout << head->_value << " ";
            head = head->_next;
        }
        std::cout<<std::endl;
    }
    else{
        std::cout<<std::endl;
        return;
    }
}

main.cpp

#include <iostream>
#include "List.hpp"

int main(){
    List *L = new List();
    int N=0;
    std::cout << "type the N value"<< std::endl;
    std::cin >> N;

    for(int i=0; i<=N; i++){
        L->insert(i);
    }

    L->print();
    delete L;
    return 0;
}

控制台

▶ g++ -std=c++14 -Wall main.cpp List.cpp -o main && ./main out
List.cpp: In member function ‘void List::insert(int)’:
List.cpp:10:15: warning: ‘node’ is used uninitialized in this function [-Wuninitialized]
   10 |  node->_value = value;
      |  ~~~~~~~~~~~~~^~~~~~~
type the N value
3
[1]    13247 segmentation fault (core dumped)  ./main out

我实际上也不知道如何调试它(我正在使用 VS Code),所以我不知道在堆栈和堆上创建的变量发生了什么。

如错误(警告)消息所述,在您正在执行的 insert 函数中:

Node* node;

但这只是声明了一个尚未指向有效内存的指针。访问对象的成员,例如 node 指向的 _value 将调用未定义的行为。这可能会导致分段错误。如果你运气不好,不会出现段错误,程序会在稍后的某个时候中断。

您需要像这样为 Node 分配内存:

Node* node = new Node{};

事实上,整个 insert 函数可以简单地是:

void List::insert(int value) {
    _head = new Node{value, _head};  // allocate Node, initialize to
                                     // appropriate values, and link _head
}

此外,您应该默认初始化 Node 的成员,如下所示:

struct Node{
    int _value{};
    Node *_next = nullptr;
};

此外,main中的List似乎不​​需要分配内存:

List *L = new List();

相反,您可以像这样简单地拥有一个 List 对象:

List L{};

在成员函数内部insert你使用了一个未初始化的指针节点

void List::insert(int value){
    Node* node;
    ^^^^^^^^^^^
    node->_value = value;
    node->_next = _head;
    _head = node;
}

具有不确定的值并尝试使用导致未定义行为的指针访问内存。

您必须分配一个节点,该节点将被指针指向并插入到列表中。

你还忘了增加列表的大小。

但我想指出实施的一些缺点。

初学者不要使用以下划线开头的标识符,因为根据 C++ 标准

(3.2) — Each identifier that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

所以这样的名字会使代码的读者感到困惑。

结构 Node 应该是结构 List 的私有或受保护数据成员。用户不得直接访问结构节点。这是一个实现细节。

动态分配 List 类型的对象是没有意义的。

这是一个演示程序,展示了如何实现该列表。

#include <iostream>
#include <functional>

class List
{
protected:
    struct Node
    {
        int value;
        Node *next;
    } *head = nullptr;

    size_t n = 0;

public: 
    List() = default;
    ~List() { clear(); }
    
    //  These special member functions you can define yourself if you will want
    List( const List & ) = delete;
    List & operator =( const List & ) = delete;
    

    void insert( int value );

    size_t size() const { return n; }
    
    void clear()
    {
        while ( head ) delete std::exchange( head, head->next );
        n = 0;
    }
    
    friend std::ostream & operator <<( std::ostream &os, const List &list )
    {
        for ( Node *current = list.head; current != nullptr; current = current->next )
        {
            os << current->value << " -> ";
        }
        
        return os << "null";
    }
};

void List::insert( int value )
{
    head = new Node { value, head };
    ++n;
}

int main() 
{
    const int N = 10;
    
    List list;
    
    for ( int i = N; i != 0; i-- )
    {
        list.insert( i );
    }
    
    std::cout << list << '\n';
    
    return 0;
}

程序输出为

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> null