返回0后的段错误;

时间:2009-06-14 19:41:24

标签: recursion segmentation-fault

我写了一个程序来测试我的二叉树,当我运行它时,程序似乎崩溃了(btree.exe已经停止工作,Windows正在检查解决方案......)。

当我通过我的调试器运行并将断点放在我怀疑导致它的函数destroy_tree()时,它似乎按预期运行并返回到main函数。反过来,Main从程序返回,但随后光标跳回到destroy_tree()并在其内部重复循环。

最小代码示例位于下方,因此可以立即运行。我的编译器是MinGW,我的调试器是gdb(我正在使用Code :: Blocks)。

#include <iostream>

using namespace std;

struct node
{
    int key_value;
    node *left;
    node *right;
};

class Btree
{
public:
    Btree();
    ~Btree();
    void insert(int key);
    void destroy_tree();

private:
    node *root;

    void destroy_tree(node *leaf);
    void insert(int key, node *leaf);
};

Btree::Btree()
{
    root = NULL;
}

Btree::~Btree()
{
    destroy_tree();
}

void Btree::destroy_tree()
{
    destroy_tree(root);

    cout<<"tree destroyed\n"<<endl;
}

void Btree::destroy_tree(node *leaf)
{
  if(leaf!=NULL)
  {
    destroy_tree(leaf->left);
    destroy_tree(leaf->right);
    delete leaf;
  }
}

void Btree::insert(int key, node *leaf)
{
    if(key < leaf->key_value)
    {
        if(leaf->left!=NULL)
            insert(key, leaf->left);
        else
        {
            leaf->left = new node;

            leaf->left->key_value = key;
            leaf->left->left = NULL;
            leaf->left->right = NULL;
        }
    }
    else if (key >= leaf->key_value)
    {
        if(leaf->right!=NULL)
            insert(key, leaf->right);
        else
        {
            leaf->right = new node;

            leaf->right->key_value = key;
            leaf->right->left = NULL;
            leaf->right->right = NULL;
        }
    }
}

void Btree::insert(int key)
{
    if(root!=NULL)
    {
        insert(key, root);
    }
    else
    {
        root = new node;

        root->key_value = key;
        root->left = NULL;
        root->right = NULL;
    }
}

int main()
{
    Btree tree;
    int i;

    tree.insert(1);

    tree.destroy_tree();

    return 0;
}

另外,我打算从Code :: Blocks内置调试器切换到DDD来调试这些问题。我听说DDD可以直观地显示指向对象的指针而不只是显示指针的地址。您认为制作开关有助于解决这些类型的问题(数据结构和算法问题)吗?

4 个答案:

答案 0 :(得分:4)

你的destroy_tree()被调用两次,你调用它一次,然后在执行离开析构函数的main()后调用它。

你可能认为它应该工作,因为你检查leaf!= NULL,但是delete没有将指针设置为NULL。所以当第二次调用destroy_tree()时,你的root不是NULL,

答案 1 :(得分:0)

与您的问题没有直接关系(或者可能是),但为结构提供构造函数是一种好习惯。例如:

struct node
{
    int key_value;
    node *left;
    node *right;

    node( int val ) : key_val( val ), left(NULL), right(NULL) {}
};

如果这样做,您的代码会变得更简单,因为您在创建节点时无需担心设置指针,并且不可能忘记初始化它们。

关于DDD,它是一个很好的调试器,但坦率地说,调试的秘诀是首先编写正确的代码,所以你不必这样做。 C ++在这方面为您提供了很多帮助(比如使用构造函数),但您必须了解并使用它提供的工具。

答案 2 :(得分:0)

Btree :: destroy_tree在成功核对树之后没有将'root'设置为0。结果,析构函数类再次破坏destroy_tree(),并且您正在尝试销毁已经销毁的对象。

那将是未定义的行为:)。

答案 3 :(得分:0)

一旦你摧毁根。
确保它是NULL,因此它不会再尝试(从析构函数)

void Btree::destroy_tree(node *leaf)
{
  if(leaf!=NULL)
  {
    destroy_tree(leaf->left);
    destroy_tree(leaf->right);
    delete leaf;

    leaf = NULL; // add this line
  }
}