노드가 수학 연산자 또는 상수가 될 수있는 무작위로 선택된 노드를 사용하여 균형 이진 트리를 작성해야하는이 프로젝트를 수행하고 있습니다. createRandomNode (bool, node *) 함수는 기본적으로 무작위로 선택된 노드를 반환합니다.반복적 인 이진 트리 생성시 노드의 포인터를 할당 할 때 오류가 발생했습니다.
문제는 VS에서이 코드를 실행할 때 완벽하게 실행할 수 있다는 것입니다. 그러나 우분투로 가져 오면 return retFtn이 else 세그먼트에서 실행될 때 반환 된 노드의 포인터 값이 변경됩니다.
그래서 VS에서 예를 들어, 나무가 기본적으로 아래, 나는 각 노드 대표하는 포인터에 저장된 주소를 사용하는 경우처럼 보일 것입니다 : 각 노드에 대해
0x0543b
/ \
0x0544c 0x0456d
/ \ / \
0x0342d 0x0453c 0x665f 0x893a
을, 나는 그것의 가치를 볼 수 있습니다 VS에서 실행할 때 레이블. 그러나 우분투에서 실행될 때 순간 0x0453c가 반환되고 재귀 함수가 retFtn-> right (여기서 retFtn은 0x0544c가 이미 완료되었으므로 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;
}
}
내가 분명히 설명했으면 좋겠습니다. :)
/******************************** ****************************/ 편집 :
다음은 재생성 할 수있는 최소의 완전하고 검증 가능한 예입니다. 우분투 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
메이크 :
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
디버거를 사용하여 코드를 단계별로 실행하는 방법을 배워야 할 수도 있습니다. 좋은 디버거를 사용하면 한 줄씩 프로그램을 실행하고 예상 한 곳에서 벗어난 곳을 볼 수 있습니다. 프로그래밍을 할 때 필수적인 도구입니다. 추가 읽기 : ** [작은 프로그램 디버깅 방법] (http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) ** – NathanOliver
당신은 가능한 실행 경로가 있습니다. 정의되지 않은 값을 반환합니다. if (... == NULL) return ...; 그렇지 않니? 무엇을 돌려 주시겠습니까? –
@ NathanOliver hmmm 실제로, 디버거를 사용하고 코드의 모든 단일 행에 대해 중단 점을 설정 했으므로 주소 0x0544c가 retFtn-> right 행에서 정확하게 변경되었음을 알았습니다. else에서 retFtn (0x0453c)을 반환 한 후 세그먼트, 그래서 나는 잘못 된 주위에 내 머리를 감쌀 수 없었다. : – programmer