【问题标题】:Why does this code run for VS but not gcc/g++?为什么这段代码适用于 VS 而不是 gcc/g++?
【发布时间】:2020-11-13 23:55:23
【问题描述】:

我正在尝试使用 VS 2019 作为我的 IDE 来实现红黑树。它似乎在 VS 中工作,但是当我尝试使用其他任何东西编译和运行时,每当我的插入函数被多次调用时,它都会导致段错误。 (我尝试过在线编译器并将其发送给朋友)我被困住了,因为我不知道从哪里开始尝试修复我的错误。我听说 VS 处理动态内存的方式不同,但我不太确定。

下面是我的旋转、BST 插入和插入函数。

#include <iostream>
#include <vector>

// default constructor
// creates empty tree, root is nullptr

class NodeT
{
public:
    // public variables
    double key;
    double value;
    NodeT* left;
    NodeT* right;
    NodeT* parent;
    bool isBlack;

    // constructors
    NodeT()
    {
        key = 0;
        value = 0;
        left = nullptr;
        right = nullptr;
        parent = nullptr;
        isBlack = false;
    }
    NodeT(double keyset, double valueset, bool isBlackset)
    {
        key = keyset;
        value = valueset;
        left = nullptr;
        right = nullptr;
        parent = nullptr;
        isBlack = isBlackset;
    }
};

class RedBlackTree
{
public:

    // default constructor, sets all to null
    RedBlackTree();

    // insert
    // inserts the first parameter key, and value second parameter into the tree
    // returns true if done, false if there are duplicates, dont insert
    bool insert(double insert_key, double insert_value);

    
public:
    NodeT* root;
    void leftrotate(NodeT* to_rotate);
    void rightrotate(NodeT* to_rotate);
    bool bstinsert(NodeT* insert);

};

RedBlackTree::RedBlackTree()
{
    root = nullptr;
}


void RedBlackTree::leftrotate(NodeT* to_rotate)
{
    NodeT* new_parent = nullptr;
    new_parent = to_rotate->right;
    to_rotate->right = new_parent->left;
    if (new_parent->left != nullptr)
    {
        new_parent->left->parent = to_rotate;
    }
    new_parent->parent = to_rotate->parent;
    if (to_rotate->parent == nullptr)
    {
        root = new_parent;
    }
    else if (to_rotate == to_rotate->parent->left)
    {
        to_rotate->parent->left = new_parent;
    }
    else
    {
        to_rotate->parent->right = new_parent;
    }
    new_parent->left = to_rotate;
    to_rotate->parent = new_parent;
}

void RedBlackTree::rightrotate(NodeT* to_rotate)
{
    NodeT* new_parent = to_rotate->left;
    to_rotate->left = new_parent->right;
    if (new_parent->right != nullptr)
    {
        new_parent->right->parent = to_rotate;
    }
    new_parent->parent = to_rotate->parent;
    if (to_rotate->parent == nullptr)
    {
        root = new_parent;
    }
    else if (to_rotate == to_rotate->parent->right)
    {
        to_rotate->parent->right = new_parent;
    }
    else
    {
        to_rotate->parent->left = new_parent;
    }
    new_parent->right = to_rotate;
    to_rotate->parent = new_parent;
}

bool RedBlackTree::bstinsert(NodeT* insert)
{
    NodeT* parent = root;
    NodeT* search = root;

    if (root == nullptr)
    {
        root = insert;  
    }
    else
    {
        while (search != nullptr)
        {
            parent = search;
            if (insert->key < parent->key)
            {
                search = parent->left;
            }
            else if (insert->key > parent->key)
            {
                search = parent->right;
            }
            else                
            {
                return false;
            }
        }
        if (insert->key < parent->key)
        {
            parent->left = insert;
            insert->parent = parent;
            return true;
        }
        else if(insert->key > parent->key)
        {
            parent->right = insert;
            insert->parent = parent;
            return true;
        }
        else
        {
            return false;
        }
    }
}

bool RedBlackTree::insert(double insert_key, double insert_value)
{
    NodeT* y = nullptr;
    NodeT* x = new NodeT(insert_key, insert_value, false);
    bool dupe = bstinsert(x);
    if (dupe == false)
    {
        return false;
    }
    
    while (x != root && x->parent->isBlack == false)
    {
        if (x->parent == x->parent->parent->left)
        {
            y = x->parent->parent->right;
            if (y == nullptr || y->isBlack == true)
            {
                if (x == x->parent->right)
                {
                    x = x->parent;
                    leftrotate(x);
                }
                x->parent->isBlack = true;
                x->parent->parent->isBlack = false;
                rightrotate(x->parent->parent);
            }
            else if (y->isBlack == false)
            {
                x->parent->isBlack = true;
                y->isBlack = true;
                x->parent->parent->isBlack = false;
                x = x->parent->parent;
            }
        }
        else
        {
            y = x->parent->parent->left;
            if (y == nullptr || y->isBlack == true)
            {
                if (x == x->parent->left)
                {
                    x = x->parent;
                    rightrotate(x);
                }
                x->parent->isBlack = true;
                x->parent->parent->isBlack = false;
                leftrotate(x->parent->parent);
            }
            else if (y->isBlack == false)
            {
                x->parent->isBlack = true;
                y->isBlack = true;
                x->parent->parent->isBlack = false;
                x = x->parent->parent;
            }
        }
    }
    root->isBlack = true;
    return true;
}

在 main 中调用它会导致段错误,除非在 VS 中:

int main()
{
    RedBlackTree test;
    test.insert(47, 1);
    test.insert(32, 2);
}

感谢您抽出宝贵时间阅读本文。任何帮助表示赞赏。

【问题讨论】:

  • 您是否尝试过使用调试器单步执行您的insert 成员函数,看看发生了什么? VS 有一个内置的 Windows 调试器
  • @Kaldrr 是的,在 VS 上我看不出有什么问题,我可以插入节点,它们似乎工作正常。在另一个编译器上插入多个节点是问题发生的地方。
  • 请发帖minimal reproducible example。这意味着我们应该能够获取您的代码,并且无需任何更改即可复制问题。目前,我们不知道RedBlackTree 的构造函数做什么或不做什么。
  • 这看起来有点可疑:if (x-&gt;parent == x-&gt;parent-&gt;parent-&gt;left) 在 insert() 函数中。你确定父级两层深总是初始化吗? (我看不到填充它的代码)并且它可能未定义/为空。否则我们将需要完整的代码。这在哪些编译器上失败? - 例如在带有 gcc 的 linux 中,您可以使用 vscode + gdb 之类的 IDE 来调试它。
  • 这些指针很可能没有初始化。如果是这样,Visual Studio 版本也坏了。也许如果你运行一个发布版本,你会发现有问题。

标签: c++ red-black-tree


【解决方案1】:

至少有一个错误会导致未定义的行为:

RedBlackTree::bstinsert 函数在应该返回 bool 时未能返回值。

要验证这是错误,可以在bstinsert 函数末尾之前放置一行来验证这是一个错误。

bool RedBlackTree::bstinsert(NodeT* insert)
{
    NodeT* parent = root;
    NodeT* search = root;

    if (root == nullptr)
    {
        root = insert;
    }
    else
    {
        while (search != nullptr)
        {
            parent = search;
            if (insert->key < parent->key)
            {
                search = parent->left;
            }
            else if (insert->key > parent->key)
            {
                search = parent->right;
            }
            else
            {
                return false;
            }
        }
        if (insert->key < parent->key)
        {
            parent->left = insert;
            insert->parent = parent;
            return true;
        }
        else if (insert->key > parent->key)
        {
            parent->right = insert;
            insert->parent = parent;
            return true;
        }
        else
        {
            return false;
        }
    }
    std::cout << "This is undefined behavior\n";  // <-- add this line
}

您将看到std::cout 行,以确认您正在从bstinsert 返回而不返回值。

此外,您使用的编译器 (Visual Studio) 会就此问题向您发出警告。类似的东西:

warning C4715: 'RedBlackTree::bstinsert': not all control paths return a value

您不应该忽略此警告。

【讨论】:

  • 值得一提的是,实际编译和运行这样的东西意味着 OP 故意忽略编译器的诊断输出。编译器通常会在遇到无法返回声明类型 :) 的值的函数时抱怨
  • 啊。我以为我测试了 bstinsert 所以我不认为这是问题所在。我认为 bstinsert 现在返回正确的值,现在我在主插入函数中有一个可重现的错误,我现在可以尝试修复它。感谢您的所有帮助!
  • @K.Lo 仅供参考——不要修复代码并在发布模式下重新编译并运行它。您应该会看到抛出的异常。这正是未定义行为的含义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-15
  • 1970-01-01
  • 1970-01-01
  • 2020-09-07
  • 1970-01-01
  • 2016-04-08
  • 2022-12-20
相关资源
最近更新 更多