【问题标题】:Head pointer pointing to garbage value after deleting node from head in singly linked list从单链表的头中删除节点后指向垃圾值的头指针
【发布时间】:2019-09-05 08:01:25
【问题描述】:

我正在尝试在 C++ 中实现单链表。在delete temp; 声明之前,deleteFromHead() 中的一切工作正常。在声明之后,headnext 都开始指向一些垃圾值。由于这是从头部删除节点的标准方法,所以我无法找出问题所在。 完整代码如下:

#include <iostream>
#include <iomanip>
#include <limits>

#define CLEAR_INPUT_BUFFER(c) \
    c.clear(); \
    c.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

template <class T>
class SLLNode {
public:
    T data;
    SLLNode<T>* next;

    SLLNode(void) {
        data = static_cast<T>(0);
        next = NULL;
    }

    SLLNode(T data) {
        this -> data = data;
        next = NULL;
    }

    ~SLLNode(void) {
        delete next;
    }
};

template <class T>
class SLList {
private:
    SLLNode<T>* head;
    SLLNode<T>* tail;
public:
    SLList(void) {
        head = tail = NULL;
    }

    ~SLList(void) {
        delete head;
        delete tail;
    }

    int addAtHead(T data) {
        SLLNode<T>* temp;
        if(!(temp = new SLLNode<T>(data))) {
            return -1;
        }
        if(head == NULL && tail == NULL) {
            head = temp;
            tail = temp;
        } else {
            temp -> next = head;
            head = temp;
        }
        return 0;
    }

    int addAtTail(T data) {
        SLLNode<T>* temp;
        if(!(temp = new SLLNode<T>(data))) {
            return -1;
        }
        if(head == NULL && tail == NULL) {
            head = temp;
            tail = temp;
        } else {
            tail -> next = temp;
            tail = temp;
        }
        return 0;
    }

    int deleteFromHead(void) {
        if(head == NULL && tail == NULL) {
            return -1;
        } else if(head == tail) {
            delete head;
            head = NULL;
            tail = NULL;
       } else {
            std::cout << "\nhere\n";
            SLLNode<T>* temp = head;
            std::cout << "temp -> data = " << temp -> data;
            std::cout << "\nhead -> data = " << head -> data;
            std::cout << "\n\nhead -> next -> data = " << head -> next -> data;
            std::cout << "\ntemp -> next -> data = " << temp -> next -> data;
            head = head -> next;
            std::cout << "\n\nafter head = head -> next:";
            std::cout << "\nhead -> data = " << head -> data;
            std::cout << "\ntemp -> data = " << temp -> data;
            delete temp;
            std::cout << "\n\nafter delete temp:";
            std::cout << "\nhead -> data = " << head -> data;
            std::cout << "\ntemp -> data = " << temp -> data;
       }
       return 0;
    }

    int deleteFromTail(void) {
        if(head == NULL && tail == NULL) {
            return -1;
        } else if(head == tail) {
            delete tail;
            head = NULL;
            tail = NULL;
        } else {
            SLLNode<T>* temp = head;
            while(temp -> next -> next != NULL) {
                temp = temp -> next;
            }
            temp -> next = NULL;
            delete tail;
            tail = temp;
        }
        return 0;
    }

    void traverse(void) {
        if(head == NULL && tail == NULL) {
            std::cout << "\nNo node present in the linked list.";
            return;
        } else {
            SLLNode<T>* temp = head;
            std::cout << "\ntemp -> data = " << temp -> data << std::endl;
            while(temp != NULL) {
                std::cout << temp -> data << "  ";
                temp = temp -> next;
            }
        }
        return;
    }

    int search(T data) {
        if(head == NULL && tail == NULL) {
            return -2;
        } else {
            int index = 1;
            SLLNode<T>* temp = head;
            while(temp != NULL) {
                if(temp -> data == data) {
                    return index;
                } else {
                    ++index;
                    temp = temp -> next;
                }
            }
        }
        return -1;
    }
};

int main(void) {
    int choice;
    SLList<int> sll;

    while(true) {
        std::cout << "\t\tSINGLY LINKED LIST DEMO PROGRAM\n\n\t\t"
                << "----------MENU----------\n\t\t"
                << "1. Add at head\n\t\t"
                << "2. Add at tail\n\t\t"
                << "3. Delete from head\n\t\t"
                << "4. Delete from tail\n\t\t"
                << "5. Traverse the list\n\t\t"
                << "6. Search the list\n\t\t"
                << "7. Exit the program\n\n"
                << "Enter your choice: ";
        while(!(std::cin >> choice) || !(choice >= 1 && choice <= 7)) {
            std::cout << "Please enter a valid choice: ";
            CLEAR_INPUT_BUFFER(std::cin);
        }
        CLEAR_INPUT_BUFFER(std::cin);

        switch(choice) {
            case 1 : {
                int data;

                std::cout << "Enter the data (any number): ";
                while(!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }
                CLEAR_INPUT_BUFFER(std::cin);
                if(sll.addAtHead(data) == -1) {
                    std::cerr << "\nERROR: Memory could not be allocated\n";
                    exit(-1);
                }
            }
            break;

            case 2 : {
                int data;

                std::cout << "Enter the data (any number): ";
                while(!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }
                CLEAR_INPUT_BUFFER(std::cin);
                if(sll.addAtTail(data) == -1) {
                    std::cerr << "\nERROR: Memory could not be allocated\n";
                    exit(-1);
                }
            }
            break;

            case 3 : {
                if(sll.deleteFromHead() == -1) {
                    std::cout << "\nLinked list is empty!";
                }
            }
            break;

            case 4 : {
                if(sll.deleteFromTail() == -1) {
                    std::cout << "\nLinked list is empty!";
                }
            }
            break;

            case 5 : {
                std::cout << "\nLinked List:\n";
                sll.traverse();
            }
            break;

            case 6 : {
                int data, index;

                std::cout << "Enter the data to be searched (any number) : ";
                while(!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }
                CLEAR_INPUT_BUFFER(std::cin);
                index = sll.search(data);
                if(index == -1) {
                    std::cout << "\nValue not found.\n";
                } else if(index == -2) {
                    std::cout << "\nNo node is present in the linked list.\n";
                } else {
                    std::cout << "\nValue found at node number: " << index;
                }
            }
            break;

            case 7 : {
                // code
            }
            break;
        }
    } 
    return 0;
}

链表为:68 -&gt; 59 -&gt; 32 -&gt; 74 -&gt; 86

输出是: 请帮忙。

【问题讨论】:

  • 请将您的代码发布为代码,而不是图像。
  • 请不要张贴文字图片。并了解minimal reproducible example 是什么。
  • 当您假设问题出在某个特定位置时,通常情况并非如此——特别是如果您不知道什么问题出在哪里。花点时间想想你销毁节点的方法是做什么的。
  • 空参数列表一般写成()(void) 适用于 C 程序员。

标签: c++ pointers singly-linked-list


【解决方案1】:

您确实应该使用标准的std::list(双链接)或std::forward_list(单链接)容器,而不是手动实现链接列表。特别是因为您的实现中存在逻辑错误。

话虽如此,SLLNode 类的递归析构函数是一个非常糟糕的主意,你需要摆脱它。它可能导致大型列表的堆栈溢出。但更重要的是,这是你的垃圾问题的根本原因。它正在删除您之后尝试访问的节点。

具体

  • 在您的 SLList 析构函数中,调用 delete head 会释放整个节点列表,在调用 delete tail 之前使 tail 指针无效(您根本不应该调用它)。

  • 在您的deleteFromHead() 方法中,您在更新head 指针之前持有指向原始head 节点的temp 指针,然后您调用delete temp 而不将temp-&gt;next 重置为首先为 NULL,因此您正在清除整个节点列表,使 headtail 指针无效。

永远不要使用递归析构函数! SLLNode 不应该负责释放列表中的下一个节点。在不再使用时释放每个节点应该是SLList 的责任。要清除整个节点列表,请让 SLList 使用迭代循环而不是递归循环。

尝试类似的方法:

#include <iostream>
#include <iomanip>
#include <limits>
#include <new>

#define CLEAR_INPUT_BUFFER(c) \
    c.clear(); \
    c.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

template <class T>
class SLLNode {
public:
    T data;
    SLLNode<T>* next;

    SLLNode() : data(), next(NULL) {}
    SLLNode(const T &data) : data(data), next(NULL) {}
};

template <class T>
class SLList {
private:
    SLLNode<T>* head;
    SLLNode<T>* tail;

    SLList(const SLList&) {}
    SLList& operator=(const SLList&) {}

public:
    SLList() : head(NULL), tail(NULL) {}

    ~SLList() {
        clear();
    }

    void clear() {
        SLLNode<T> *temp = head;
        head = tail = NULL;
        while (temp) {
            SLLNode<T> *next = temp->next;
            delete temp;
            temp = next;
        }
    }

    int addAtHead(const T &data) {
        SLLNode<T>* temp = new(std::nothrow) SLLNode<T>(data);
        if (!temp) {
            return -1;
        } 
        temp->next = head;
        head = temp;
        if (!tail) {
            tail = temp;
        }
        return 0;
    }

    int addAtTail(const T &data) {
        SLLNode<T>* temp = new(std::nothrow) SLLNode<T>(data);
        if (!temp) {
            return -1;
        }
        if (!head) {
            head = temp;
        }
        if (tail) {
            tail->next = temp;
        }
        tail = temp;
        return 0;
    }

    int deleteFromHead() {
        if (!head) {
            return -1;
        }
        SLLNode<T>* temp = head;
        if (head == tail) {
            tail = NULL;
        }
        head = head->next;
        delete temp;
        return 0;
    }

    int deleteFromTail() {
        if (!head) {
            return -1;
        }
        SLLNode<T>* temp = head, *newTail = NULL;
        while (temp->next) {
            newTail = temp;
            temp = temp->next;
        }
        if (newTail) {
            newTail->next = NULL;
        }
        if (head == tail) {
            head = NULL;
        }
        tail = newTail;
        delete temp;
        return 0;
    }

    void traverse() {
        if (!head) {
            std::cout << "\nNo node present in the linked list." << std::endl;
            return;
        }
        SLLNode<T>* temp = head;
        std::cout << "\n" << temp->data;
        while (temp->next) {
            temp = temp->next;
            std::cout << " " << temp->data;
        }
        std::cout << std::endl;
    }

    int search(const T &data) {
        if (!head) {
            return -2;
        }
        int index = 0;
        SLLNode<T>* temp = head;
        do {
            if (temp->data == data) {
                return index;
            }
            ++index;
            temp = temp->next;
        }
        while (temp);
        return -1;
    }
};

int main() {
    int choice;
    SLList<int> sll;

    do {
        std::cout << "\t\tSINGLY LINKED LIST DEMO PROGRAM\n\n\t\t"
                    << "----------MENU----------\n\t\t"
                    << "1. Add at head\n\t\t"
                    << "2. Add at tail\n\t\t"
                    << "3. Delete from head\n\t\t"
                    << "4. Delete from tail\n\t\t"
                    << "5. Traverse the list\n\t\t"
                    << "6. Search the list\n\t\t"
                    << "7. Exit the program\n\n"
                    << "Enter your choice: ";

        while (!(std::cin >> choice) || !(choice >= 1 && choice <= 7)) {
            std::cout << "Please enter a valid choice: ";
            CLEAR_INPUT_BUFFER(std::cin);
        }

        switch (choice) {
            case 1 : {
                int data;

                std::cout << "Enter the data (any number): ";
                while (!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }

                if (sll.addAtHead(data) == -1) {
                    std::cerr << "\nERROR: Memory could not be allocated\n";
                    return -1;
                }
            }
            break;

            case 2 : {
                int data;

                std::cout << "Enter the data (any number): ";
                while (!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }

                if (sll.addAtTail(data) == -1) {
                    std::cerr << "\nERROR: Memory could not be allocated\n";
                    return -1;
                }
            }
            break;

            case 3 : {
                if (sll.deleteFromHead() == -1) {
                    std::cout << "\nLinked list is empty!";
                }
            }
            break;

            case 4 : {
                if (sll.deleteFromTail() == -1) {
                    std::cout << "\nLinked list is empty!";
                }
            }
            break;

            case 5 : {
                std::cout << "\nLinked List:\n";
                    sll.traverse();
            }
            break;

            case 6 : {
                int data, index;

                std::cout << "Enter the data to be searched (any number) : ";
                while (!(std::cin >> data)) {
                    std::cout << "Please enter a valid number: ";
                    CLEAR_INPUT_BUFFER(std::cin);
                }

                index = sll.search(data);
                if (index == -2) {
                    std::cout << "\nNo node is present in the linked list.\n";
                }
                else if (index == -1) {
                    std::cout << "\nValue not found.\n";
                }
                else {
                    std::cout << "\nValue found at node number: " << index;
                }
            }
            break;
        }
    }
    while (choice != 7);

    return 0;
}

【讨论】:

  • 那么如何在这个程序中管理内存呢?通过指针分配的内存必须在程序终止时销毁。附:这是我正在实施链接列表的练习。我知道 std::list
  • @ShashankKadambri 查看我添加的代码。释放节点是SLList的责任,而不是SLLNode的责任
  • 非常感谢!我从这段代码中学到了很多东西。你为什么要单独创建一个clear()?为什么不在析构函数本身中实现相同的代码?
  • @ShashankKadambri 可能有,但想要清除列表而不完全破坏它的情况并不少见。而且因为小的目标函数更容易测试和调试。查看 Single Responsibility PrincipleSeparation of Concerns 之类的内容
猜你喜欢
  • 1970-01-01
  • 2021-01-15
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多