链表 (c++) 的分段错误

Segmentation fault with linked list (c++)

本文关键字:分段 错误 c++ 链表      更新时间:2023-10-16

当以下程序运行时,我得到一个分段错误。 问题到底出在哪里?

头文件:

#ifndef MAIN_SAVITCH_NODE1_H  
#define MAIN_SAVITCH_NODE1_H
#include <cstdlib> // Provides size_t and NULL
namespace main_savitch_5
{
    class node
    {
    public:
        // TYPEDEF
        typedef double value_type;
        // CONSTRUCTOR
        node(
            const value_type& init_data = value_type(),
            node* init_link = NULL
            )
        {
            data_field = init_data; link_field = init_link;
        }
        // Member functions to set the data and link fields:
        void set_data(const value_type& new_data) { data_field = new_data; }
        void set_link(node* new_link)             { link_field = new_link; }
        // Constant member function to retrieve the data:
        value_type data() const { return data_field; }
        // Constant member functions to retrieve the link:
        node* link() const          { return link_field; }
    private:
        value_type data_field;
        node* link_field;
    };
    // FUNCTIONS for the linked list toolkit
    std::size_t list_length(const node* head_ptr);
    void list_head_insert(node*& head_ptr, const node::value_type& entry);
    void list_insert(node* previous_ptr, const node::value_type& entry);
    node* list_search(node* head_ptr, const node::value_type& target);
    node* previous_to_min(node* marker_ptr);
    node* list_locate(node* head_ptr, std::size_t position);
    void list_head_remove(node*& head_ptr);
    void list_remove(node* previous_ptr);
    void list_clear(node*& head_ptr);
    void list_copy(const node* source_ptr, node*& head_ptr, node*& tail_ptr);
    void print_list(const node* head_ptr);
}
#endif

实现文件(这些都不是我的)

#include "node1.h"
#include <cassert>    // Provides assert
#include <iostream>  
#include <cstdlib>    // Provides NULL and size_t
using namespace std;
namespace main_savitch_5
{
    size_t list_length(const node* head_ptr)
        // Library facilities used: cstdlib
    {
        const node *cursor;
        size_t answer;
        answer = 0;
        for (cursor = head_ptr; cursor != NULL; cursor = cursor->link())
            ++answer;
        return answer;
    }
    void print_list(const node* head_ptr)
    {
        const node *cursor;
        cout << endl;
        for (cursor = head_ptr; cursor != NULL; cursor = cursor->link()) cout << cursor->data() << " ";
        cout << endl;
    }
    void list_head_insert(node*& head_ptr, const node::value_type& entry)
    {
        head_ptr = new node(entry, head_ptr);
    }
    void list_insert(node* previous_ptr, const node::value_type& entry)
    {
        node *insert_ptr;
        insert_ptr = new node(entry, previous_ptr->link());
        previous_ptr->set_link(insert_ptr);
    }
    node* list_search(node* head_ptr, const node::value_type& target)
        // Library facilities used: cstdlib
    {
        node *cursor;
        for (cursor = head_ptr; cursor != NULL; cursor = cursor->link())
            if (target == cursor->data())
                return cursor;
        return NULL;
    }
    node* previous_to_min(node* marker_ptr)
    {
        node * cursor;
        node * pre_cursor;
        node * current_min_prev_cursor;
        int current_min;
        pre_cursor = marker_ptr;
        cursor = marker_ptr->link();
        current_min = cursor->data();
        current_min_prev_cursor = pre_cursor;
        while (cursor != NULL)
        {
            if (cursor->data() < current_min)
            {
                current_min = cursor->data();
                current_min_prev_cursor = pre_cursor;
            }
            pre_cursor = cursor;
            cursor = cursor->link();
        }
        return current_min_prev_cursor;
    }





    node* list_locate(node* head_ptr, size_t position)
        // Library facilities used: cassert, cstdlib
    {
        node *cursor;
        size_t i;
        assert(0 < position);
        cursor = head_ptr;
        for (i = 1; (i < position) && (cursor != NULL); i++)
            cursor = cursor->link();
        return cursor;
    }

    void list_head_remove(node*& head_ptr)
    {
        node *remove_ptr;
        remove_ptr = head_ptr;
        head_ptr = head_ptr->link();
        delete remove_ptr;
    }
    void list_remove(node* previous_ptr)
    {
        node *remove_ptr;
        remove_ptr = previous_ptr->link();
        previous_ptr->set_link(remove_ptr->link());
        delete remove_ptr;
    }
    void list_clear(node*& head_ptr)
        // Library facilities used: cstdlib
    {
        while (head_ptr != NULL)
            list_head_remove(head_ptr);
    }
    void list_copy(const node* source_ptr, node*& head_ptr, node*& tail_ptr)
        // Library facilities used: cstdlib
    {
        head_ptr = NULL;
        tail_ptr = NULL;
        // Handle the case of the empty list.
        if (source_ptr == NULL)
            return;
        // Make the head node for the newly created list, and put data in it.
        list_head_insert(head_ptr, source_ptr->data());
        tail_ptr = head_ptr;
        // Copy the rest of the nodes one at a time, adding at the tail of new list.
        source_ptr = source_ptr->link();
        while (source_ptr != NULL)
        {
            list_insert(tail_ptr, source_ptr->data());
            tail_ptr = tail_ptr->link();
            source_ptr = source_ptr->link();
        }
    }
}

和我的主文件

    #include <cstdlib>
#include <ctime>
#include <iostream>
#include <cassert>
#include "node1.h"
using namespace main_savitch_5;
void list_sort(node* &head_ptr);
int main()
{
    unsigned seed = time(0);
    size_t n;
    node *head;
    node *current;
    srand(seed);
    std::cout << "How many items would you like to sort?n";
    std::cin >> n;
    assert(n > 0); // Ensure the list is not empty
    list_head_insert(head, 1 + rand() % 1000);
    current = head;
    for (size_t i = 1; i < n; i++)
    {
        list_insert(current, (1 + rand() % 1000));
        current = current->link();
    }
    std::cout << "Unsorted list:n";
    print_list(head);
    list_sort(head);
    std::cout << "Sorted list:n";
    print_list(head);
    list_clear(head);
    return 0;
}
    void list_sort(node* &head_ptr)
    {
        node* marker;
        node* min_node;
        node* temp;
        node* cursor;
        min_node = previous_to_min(head_ptr);
        // Set a pointer to the minimum data value in the list
        if (head_ptr->data() < min_node->data())
            marker = head_ptr;
        else
        {
            // If the head pointer does not hold the smallest value,
            // swap it with the node containing the min value
            temp = min_node->link();
            min_node->set_link(temp->link());
            temp->set_link(head_ptr);
            head_ptr = temp;
            marker = head_ptr;
        }
        while (marker->link()->link() != NULL)
        {
            cursor = previous_to_min(marker);
            // Find the smallest value of our new sublist
            if (cursor == marker)
                marker = marker->link();
            else
            {
                // Swap the head of the sublist with the new min value
                temp = cursor->link();
                cursor->set_link(temp->link());
                temp->set_link(marker->link());
                marker->set_link(temp);
                marker = marker->link();
            }
        }
    }

分段错误在首次调用 print_list() 后立即发生。

问题是您在列表开始时的链接节点未设置为 NULL:

main函数中,您有以下内容:

node *head;
      //...
    list_head_insert(head, 1 + rand() % 1000);

但是,list_head_insert函数使用第一个参数的值来设置下一个链接。

void list_head_insert(node*& head_ptr, const node::value_type& entry)
{
    head_ptr = new node(entry, head_ptr);
}

由于指针未初始化,因此在调用 list_head_insert 之前设置的任何垃圾都用作创建新node时的head_ptr,如下所示:

node(const value_type& init_data = value_type(),
    node* init_link = NULL)
{
   data_field = init_data; 
   link_field = init_link;  // oh no.
}

最终发生的是,添加到链表中的每个新节点都依赖于前一个节点的链接值。 因此,当您构建链表时,这个错误的链接值会在节点之间传递,并最终用作列表的哨兵节点。

由于哨兵节点具有垃圾值,因此在节点为 NULL 时停止的循环都是错误的(因为没有 NULL)。

所以解决方案是在main开始时将head节点设置为 NULL:

node *head = NULL;

现在,这会将链接设置为 NULL,然后此值将用作哨兵节点。