2017-02-06 4 views
0

노드가 수학 연산자 또는 상수가 될 수있는 무작위로 선택된 노드를 사용하여 균형 이진 트리를 작성해야하는이 프로젝트를 수행하고 있습니다. 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 
+2

디버거를 사용하여 코드를 단계별로 실행하는 방법을 배워야 할 수도 있습니다. 좋은 디버거를 사용하면 한 줄씩 프로그램을 실행하고 예상 한 곳에서 벗어난 곳을 볼 수 있습니다. 프로그래밍을 할 때 필수적인 도구입니다. 추가 읽기 : ** [작은 프로그램 디버깅 방법] (http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) ** – NathanOliver

+3

당신은 가능한 실행 경로가 있습니다. 정의되지 않은 값을 반환합니다. if (... == NULL) return ...; 그렇지 않니? 무엇을 돌려 주시겠습니까? –

+0

@ NathanOliver hmmm 실제로, 디버거를 사용하고 코드의 모든 단일 행에 대해 중단 점을 설정 했으므로 주소 0x0544c가 retFtn-> right 행에서 정확하게 변경되었음을 알았습니다. else에서 retFtn (0x0453c)을 반환 한 후 세그먼트, 그래서 나는 잘못 된 주위에 내 머리를 감쌀 수 없었다. : – programmer

답변

0

전화 할 경우 createRandomTree(int depth, node* parent)depth>1이고 parent!=NULL 인 경우이 함수는 return 문을 사용하지 않습니다. 따라서 createRandomTree이 끝날 때까지 실행되면 반환 된 값이 보장되지 않은 상태에서 컨트롤이 호출자에게 다시 전달됩니다. 따라서 createRandomTree(3, NULL);은 결과적으로 임의의 가비지가 retFtn->left에 쓰여진 retFtn->left = createRandomTree(3 - 1, retFtn);으로 이어진다.

나는 강력하게 경고를 컴파일러에 더 많은 관심을 지불하는 것이 좋습니다 :

임의.CPP : 29 : 1 : 경고 : 컨트롤이 void가 아닌 함수의 끝에 도달 [-Wreturn 형]


PS 도대체가 VS. 작업 않는 이유는 insterested 수 있습니다 정확한 이유를 모르겠다. (어셈블리 코드를 검사하여 조사 할 수는 있지만, 환경이나 파고 싶지는 않다.) 일반적인 대답은 "우연히"이다. calling conventions 많은 지정할 수 있습니다

    1. 컴파일러 (분기 인해 CPU 컨베이어 아키텍처 실행을 느리게)와 같은 쓸모없는 (아무것도 변경되지 않음)과 비용이 많이 드는 if (retFtn->parent == NULL) 점검을 던졌다 : 나는 두 가지 방법을 참조 호출자에게 반환 값을 전달하는 방법. 가장 빠른 것은 CPU 레지스터를 통해 값을 전달하는 것입니다. 나는이 상황을 상상할 수 있는데, retFtn이 함수 내에서 계산을 위해 CPU 레지스터에 저장되고 함수에서 빠져 나가면 마치 retFtn이 반환 된 것처럼 보입니다. 나는 MIPS 아키텍처에서 비슷한 경우를 가졌다.

    정확한 이유를 확인하고 우리와 공유하십시오.

  • +0

    문제를 해결할 관리 :) – programmer