删除临时指针时出现segfault

Seg fault when deleting temporary pointer

本文关键字:segfault 指针 删除      更新时间:2023-10-16

我使用gdb来查找段故障的确切行。它在dequeue函数中作为注释注释。

这是整个队列类。

当队列中有两个对象和哨兵时,调用dequeue()时会出现故障。

template <typename T>
void Queue<T>::clear()
{
    while(!isEmpty())
        dequeue();
}
template <typename T>
void Queue<T>::enqueue(const T& x)
{
    if(isEmpty())
    {
        Queue<T>* temp = new Queue<T>;
        m_data = x;
        m_next = temp;
        return;
    }

    Queue<T>* temp = this;
    while(temp->m_next != NULL)
    {
        temp = temp->m_next;
    }
    Queue<T>* node = new Queue<T>();
    temp->m_data = x;
    node->m_next = temp->m_next;
    temp->m_next = node;
    return;
}
template <typename T>
void Queue<T>::dequeue()
{
    if(isEmpty())
        return;
    if(m_next != NULL)
    {
        Queue<T>* temp = m_next;
        m_data = temp->m_data;
        m_next = temp->m_next;
        delete temp; //Seg fault here
    }
    return;
}
template <typename T>
const T& Queue<T>::front() const throw (int)
{
    if(isEmpty())
        throw 0;
    return m_data;
}
template <typename T>
bool Queue<T>::isEmpty() const
{
    return (m_next==NULL);
}
template <typename T>
int Queue<T>::size() const
{
    int size = 0;
    const Queue<T>* temp = this;
    while(temp->m_next != NULL)
    {
        temp = temp->m_next;
        size++;
    }
    return size;
}

对不起,我以为我已经发布了队列类:

template <typename T>
class Queue : public AbstractQueue<T> {
public:
    Queue(){m_next = NULL;};
    virtual void clear();
    virtual void enqueue(const T& x);
    virtual void dequeue();
    virtual const T& front() const throw (int);
    virtual bool isEmpty() const;
    virtual int size() const;
    ~Queue(){
        clear();
        return;
    };
private:
    T m_data;
    Queue* m_next;
};

它继承自这个类:

template < typename T >
class AbstractQueue
{
public:
  // Purpose: clears the queue
  // Postconditions: the queue is now empty 
  // -- PURE VIRTUAL
  virtual void clear() = 0;
  // Purpose: enqueue an element into the queue
  // Parameters: x is the item to add to the queue
  // Postconditions: x is now the element at the end of the queue, 
  // -- PURE VIRTUAL
  virtual void enqueue(const T& x) = 0;
  // Purpose: dequeues 
  // Postconditions: the element formerly at the front of the queue has
  //     been removed
  // Dequeueing from an empty Queue produces no errors, queue remains empty.
  // -- PURE VIRTUAL
  virtual void dequeue() = 0;
  // Purpose: looks at the front of the queue
  // Returns: a reference to the element currently in front of the queue
  // Exception: if the queue is currently empty, throw SOMETHING!!
  // -- PURE VIRTUAL
  virtual const T& front() const = 0;  
  // Purpose: Checks if a queue is empty
  // Returns: 'true' if the queue is empty
  //     'false' otherwise  
  // -- PURE VIRTUAL
  virtual bool isEmpty() const = 0;
  // Purpose: Returns the size of a queue.
  // Returns: the number of elements in the Queue
  // -- PURE VIRTUAL
  virtual int size() const = 0;
  // ----------------
  // Purpose: Destructor
  // -- VIRTUAL
    virtual ~AbstractQueue() {};
};

在此代码中:

Queue<T>* temp = m_next;
m_data = m_next->m_data;
m_next = m_next->m_next;

你不检查m_next是否为非空(如果你在列表的末尾),所以你开始对空指针解引用,所有的赌注都取消了。

对我来说,enqueue()中的下面一行看起来有点奇怪。

Queue<T>* node = new Queue<T>();

每次都创建一个新的Queue。

意图可以是下面的吗?

T * node = new T;

好吧,我不给你鱼,我教你怎么钓鱼…

当你得到segmentation fault,这意味着操作系统已经检测到内存访问错误。这通常发生在C/c++中使用指针时。指针是非常危险的,必须小心对待。

如何检测问题发生的位置当您的程序接收到SEGFAULT时,Linux提供的信息并不多,但是它提供了很多信息。你只需要知道如何"读"它。

coredump,是发生分段故障时的内存、堆栈和变量的图片。运行它

gdb myapp core

其中myapp是应用程序的可执行文件,core是coredump。现在您将看到如下内容:

GNU gdb 19991004
Copyright 1998 Free Software ���.�
Core was generated by `testit'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /usr/lib/libstdc++-libc6.1-1.so.2...done.
Reading symbols from /lib/libm.so.6...done.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0  0x823221a in main () at blabla.c:3
10             *i++;       

它会告诉你到底是哪一行出了问题。如果您想知道您是如何到达那一行的,请键入bt这将向您展示从应用程序main()到实际故障的回溯,包括传递给函数的参数。

我认为一旦你确切地知道分割错误发生在哪里,你就会更容易解决它。

一些笔记:

  1. 如果没有创建coredump。在控制台中输入:

    ulimit -c unlimited
    
  2. 你需要使用-g来编译你的程序,以便在gdb中启用有意义的符号名称。