簡體   English   中英

遞歸二叉樹生成中分配節點指針時出錯

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

我正在做這個項目,我應該使用隨機選擇的節點創建一個平衡的二叉樹,其中一個節點可以是數學運算符或常量。 函數createRandomNode(bool,node *)基本返回一個隨機選擇的節點。

關鍵是,當我在VS中運行這段代碼時,它可以很好地運行。 但是,當我將其移交給Ubuntu時,在else段中執行return retFtn時,指向返回節點的指針值將更改。

因此,例如在VS中,樹的外觀基本上如下所示,其中我使用存儲在指針中的地址來表示每個節點:

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

對於每個節點,我可以在VS中運行時查看其值和標簽。 但是,在Ubuntu中運行時,返回的時刻為0x0453c,並且遞歸函數返回到retFtn-> right(其中,由於0x0544c已經完成,因此retFtn現在為0x0543b),0x0544c更改為某個奇怪的地址,我再也看不到該節點的值和標簽,盡管在返回0x0453c之前仍然可以看到它。

下面是有問題的代碼。

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;
    }
}

希望我已經解釋清楚,謝謝您的幫助! :)

/ ******************* *********************** /編輯:

下面是一個最小,完整和可驗證的示例,該示例可以在Ubuntu 16.04中重現該問題(奇怪的是,在VS中運行時不會發生此問題):

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:

#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

#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:

#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:

#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文件:

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

如果您調用depth>1並且parent!=NULL createRandomTree(int depth, node* parent) ,則此函數不會命中任何return語句。 因此, createRandomTree將一直執行到最后,然后將控制傳遞回調用方,而不會提供任何保證的返回值。 因此, createRandomTree(3, NULL); 導致retFtn->left = createRandomTree(3 - 1, retFtn); 結果將隨機垃圾寫入retFtn->left

我強烈建議您更加注意編譯器警告:

random.cpp:29:1:警告:控制權到達非無效函數的結尾[-Wreturn-type]


PS,您可能會感到奇怪,為什么它會在VS上正常工作。 不知道確切的原因(可以通過檢查匯編代碼來調查原因,但是我現在沒有環境,也不想立即進行研究),但是一般的答案是“偶然”。 我看到兩種可能的方式:

  1. 編譯器拋出if (retFtn->parent == NULL)檢查為無用(不做任何更改)且代價高昂(分支由於CPU傳輸器體系結構而降低了執行速度)的情況
  2. 有很多調用約定 ,它們指定了將返回值傳遞給調用者的方式。 最快的是通過CPU寄存器傳遞值。 我可以想像這種情況,當retFtn存儲在CPU寄存器中以在函數內進行計算,並且在函數退出時,看起來好像返回了retFtn 我在MIPS架構上也有類似的案例。

隨時檢查確切原因並與我們分享。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM