简体   繁体   English

递归二叉树生成中分配节点指针时出错

[英]error in allocating pointer of node in recursive binary tree generation

I'm doing this project where I am supposed to create a balanced binary tree using randomly chosen nodes, where a node can be a math operator or a constant. 我正在做这个项目,我应该使用随机选择的节点创建一个平衡的二叉树,其中一个节点可以是数学运算符或常量。 The function createRandomNode(bool, node*) basically returns a randomly chosen node. 函数createRandomNode(bool,node *)基本返回一个随机选择的节点。

The thing is, when I run this piece of code in VS, it can run perfectly well. 关键是,当我在VS中运行这段代码时,它可以很好地运行。 However, when I bring it over to Ubuntu, the pointer value to the returned node is changed when return retFtn is executed in the else segment. 但是,当我将其移交给Ubuntu时,在else段中执行return retFtn时,指向返回节点的指针值将更改。

So for instance in VS, the tree will basically look something like below, where I used the address stored in the pointer to represent each node: 因此,例如在VS中,树的外观基本上如下所示,其中我使用存储在指针中的地址来表示每个节点:

                             0x0543b
                            /       \
                      0x0544c       0x0456d
                     /       \     /       \
                 0x0342d  0x0453c 0x665f  0x893a

For each node, I can view its value and label when running in VS. 对于每个节点,我可以在VS中运行时查看其值和标签。 However when running in Ubuntu, the moment 0x0453c is returned and the recursive function returns to retFtn->right (where retFtn is now 0x0543b since 0x0544c has already completed), 0x0544c changes to some weird address and I can no longer see this node's value and label, although I can still see it before 0x0453c is returned. 但是,在Ubuntu中运行时,返回的时刻为0x0453c,并且递归函数返回到retFtn-> right(其中,由于0x0544c已经完成,因此retFtn现在为0x0543b),0x0544c更改为某个奇怪的地址,我再也看不到该节点的值和标签,尽管在返回0x0453c之前仍然可以看到它。

Below is the code in question. 下面是有问题的代码。

node* createRandomTree(int depth, node* parent)
{
    if (depth > 1)
    {
        node* retFtn = createRandomNode(true, parent);
        retFtn->left = createRandomTree(depth - 1, retFtn);
        retFtn->right = createRandomTree(depth - 1, retFtn);
        if (retFtn->parent == NULL)
            return retFtn;
    }
    else
    {
        node* retFtn = createRandomNode(false, parent);
        return retFtn;
    }
}

I hope I have explained clearly, thank you for your help! 希望我已经解释清楚,谢谢您的帮助! :) :)

/************************************************************************/ EDIT: / ******************* *********************** /编辑:

Below is a Minimal, Complete, and Verifiable example which can recreate the problem in Ubuntu 16.04 (strangely, the problem does not occur when running in VS): 下面是一个最小,完整和可验证的示例,该示例可以在Ubuntu 16.04中重现该问题(奇怪的是,在VS中运行时不会发生此问题):

main.cpp: main.cpp中:

#include "node.h"
#include "random.h"
#include <iostream>
int main(int argc, char * const argv[])
{
    node* createdTree = createRandomTree(3, NULL);
    std::cout << createdTree << endl;
    inOrder(createdTree);
}

random.cpp: random.cpp:

#include "random.h"

void inOrder(node* tree)
{
    if (!tree)
        return;
    cout << "(";
    inOrder(tree->left);
    cout << tree->label;
    inOrder(tree->right);
    cout << ")";
}

node* createRandomTree(int depth, node* parent)
{
    if (depth > 1)
    {
        node* retFtn = createRandomNode(true, parent); //isOperator==true
        retFtn->left = createRandomTree(depth - 1, retFtn);
        retFtn->right = createRandomTree(depth - 1, retFtn);
        if (retFtn->parent == NULL)
            return retFtn;
    }
    else
    {
        node* retFtn = createRandomNode(false, parent); //isOperator==true
        return retFtn;
    }
}

node* createRandomNode(bool isOperator, node* parent)
{
    int randn = -1;
    node* retFtn = NULL;

    if (isOperator)
        randn = 1;
    else
        randn = 0;

    switch (randn)
    {
        case 0:
        {
            retFtn = new ConstantValueNode(parent);
            break;
        }
        case 1:
        {
            retFtn = new AddNode(parent);
            break;
        }
        default:
        {
            cout << "invalid random number\n\n\n";
            break;
        }
    }
    return retFtn;
}

random.h random.h

#ifndef random_H
#define random_H

#include "node.h"

node* createRandomNode(bool isOperator, node* parent);
node* createRandomTree(int depth, node* parent);
void inOrder(node* tree);
#endif

node.cpp: node.cpp:

#include "node.h"

/***************/
/*Constant Node*/
/***************/
ConstantValueNode::ConstantValueNode(node* retFtn)
{
    left=NULL;
    right=NULL;
    negate_Or_Not = false;
    constVal = rand()% 21 + (-10);
    key_value = constVal;
    parent = retFtn;
    label = "Constant";
};

ConstantValueNode::ConstantValueNode(double preSetVal)
{
    left=NULL;
    right=NULL;
    negate_Or_Not = false;
    constVal = preSetVal;
    key_value = constVal;
    label = "Constant";
};

double ConstantValueNode::eval(map<string,double> inputMapping)
{
    if (negate_Or_Not) //negation is true
        return !constVal;
    else
        return constVal;
}

ConstantValueNode* ConstantValueNode::clone(node* parent_clone)
{
    ConstantValueNode* retTree = new ConstantValueNode(key_value);
    if (parent_clone != NULL)
        retTree->parent = parent_clone;
    else
        retTree->parent = NULL;
    return retTree;
}

string ConstantValueNode::getLabel()
{
    return label;
}

/**********/
/*Add Node*/
/**********/
AddNode::AddNode()
{
    label = "AddNode";
    negate_Or_Not = NULL; //will be false by default
}
AddNode::AddNode(node* retFtn)
{
    label = "AddNode";
    negate_Or_Not = NULL;
    parent = retFtn;
}
double AddNode::eval(map<string,double> inputMapping)
{
    if (left && right)
        return left->eval(inputMapping) + right->eval(inputMapping);
    else
    {
        cout << "left and right not defined in add"<<endl;
        return -1.0;
    }
}

AddNode* AddNode::clone(node* parent_clone)
{
    AddNode* retNode = new AddNode();
    retNode->left = left->clone(retNode);
    retNode->right = right->clone(retNode);
    if (parent_clone != NULL)
        retNode->parent = parent_clone;
    else
        retNode->parent = NULL;
    return retNode;
}

string AddNode::getLabel()
{
    return label;
}

node.h: node.h:

#ifndef Node_H
#define Node_H
#include<iostream>
#include<map>

using std::string; //This will allow you to use "string" as an unqualified name
                  //(resolving to "std::string")
using std::cout;
using std::endl;
using std::map;

class node
{
    // Virtual function can be overriden and the pure virtual must be implemented.
    // virtual void Function() = 0; is a pure virtual. The "= 0" indicates is purity
    public:
        bool negate_Or_Not;
        string label;
        int key_value;
        node* left;
        node* right;
        node* parent;
        virtual double eval(map<string,double> inputMapping)=0;
        virtual node* clone(node* clone)=0;
        virtual string getLabel()=0;
};

class ConstantValueNode : public node
{
    double constVal;
    public:
        ConstantValueNode(node* retFtn);
        ConstantValueNode(double preSetVal);
        virtual double eval(map<string,double> inputMapping);
        virtual ConstantValueNode* clone(node* clone);
        virtual string getLabel();
};

class AddNode : public node
{
    public:
        AddNode();
        AddNode(node* retFtn);
        virtual double eval(map<string,double> inputMapping);
        virtual AddNode* clone(node* clone);
        virtual string getLabel();
};
#endif

Makefile: Makefile文件:

CXX = g++
CXXFLAGS = -Wall -std=c++11
# ****************************************************
main: main.o node.o random.o
    $(CXX) $(CXXFLAGS) -o main main.o node.o random.o

main.o: main.cpp node.h random.h
    $(CXX) $(CXXFLAGS) -c main.cpp

node.o: node.h 
random.o: node.h random.h

If you call createRandomTree(int depth, node* parent) with depth>1 and parent!=NULL , this function doesn't hit any return statement. 如果您调用depth>1并且parent!=NULL createRandomTree(int depth, node* parent) ,则此函数不会命中任何return语句。 So createRandomTree is executed till the end, then control is passed back to caller with no guaranteed return value provided. 因此, createRandomTree将一直执行到最后,然后将控制传递回调用方,而不会提供任何保证的返回值。 So createRandomTree(3, NULL); 因此, createRandomTree(3, NULL); leads to retFtn->left = createRandomTree(3 - 1, retFtn); 导致retFtn->left = createRandomTree(3 - 1, retFtn); where random garbage is written to retFtn->left as a result. 结果将随机垃圾写入retFtn->left

I strongly recommend you to pay more attention to compiler warnings: 我强烈建议您更加注意编译器警告:

random.cpp:29:1: warning: control reaches end of non-void function [-Wreturn-type] random.cpp:29:1:警告:控制权到达非无效函数的结尾[-Wreturn-type]


PS you might be insterested why the hell does it work on VS. PS,您可能会感到奇怪,为什么它会在VS上正常工作。 Don't know exact reason (it can be investigated by inspecting assembly code, but I neither have environment nor want to dig into it right now), but the general answer is "by chance". 不知道确切的原因(可以通过检查汇编代码来调查原因,但是我现在没有环境,也不想立即进行研究),但是一般的答案是“偶然”。 I see two possible ways: 我看到两种可能的方式:

  1. compiler threw out if (retFtn->parent == NULL) check as useless (doesn't change anything) and costly (branching slows execution due to CPU conveyor architecture) 编译器抛出if (retFtn->parent == NULL)检查为无用(不做任何更改)且代价高昂(分支由于CPU传输器体系结构而降低了执行速度)的情况
  2. There are a lot of calling conventions , which specify the way to pass return value to caller. 有很多调用约定 ,它们指定了将返回值传递给调用者的方式。 The fastest is passing value via CPU registers. 最快的是通过CPU寄存器传递值。 I can imagine the situation, when retFtn is stored in CPU register for calculations within function and on function exit it looks as if retFtn was returned. 我可以想像这种情况,当retFtn存储在CPU寄存器中以在函数内进行计算,并且在函数退出时,看起来好像返回了retFtn I had similar case on MIPS architecture. 我在MIPS架构上也有类似的案例。

Feel free to check exact reason and share with us. 随时检查确切原因并与我们分享。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM