我的问题的标题可能是该网站上其他许多问题的标题,但是,我遇到的问题可能是我一生中必须面对的最奇怪的事情。
我被分配去binary search tree
为数据库设计一个,该数据库包含一长串公司的员工记录。我已经成功实施了该项目,并且一切正常,直到收到教授的电子邮件,要求班上的每个人都通过SSH登录到基于Linux的服务器,并验证项目在该处的编译和运行情况符合预期。
源代码编译的很好,但是,当我运行该应用程序并告诉它加载文件的3000条记录列表时,我遇到了segmentation fault
第543条记录(文件中的行)。
我的问题是:考虑到我的计算机上的代码运行正常,是什么可能导致此问题?
对于项目的大小,给我分配多少内存有关系吗?加载数据时是否可能内存不足?
即使我100%确信问题不在我的代码中,但我仍然认为方便您查看一段代码并尝试找出问题。
这是我的employee
课:
class employee
{
public:
employee(){}
employee(std::string first_name, std::string last_name, unsigned int ID)
{
this->first_name = first_name;
this->last_name = last_name;
this->ID = ID;
}
std::string first_name;
std::string last_name;
unsigned int ID;
employee * left, * right;
};//*root;
这是我用来将文件加载到数据库中的功能:
void createBST(bstree & tree)
{
string file_name;
cout << "Enter database file path: ";
cin >> file_name;
ifstream file_reader (file_name.c_str());
// if (file_reader == NULL)
// {
// error("\nError: Invalid file path provided!\n\n");
/// return;
// }
string line;
for (;;)
{
if (!getline(file_reader, line))
break;
string tokens[3];
split(line, tokens);
string first_name, last_name;
unsigned int ID;
last_name = tokens[0];
first_name = tokens[1];
ID = std::atoi(tokens[2].c_str());
// cout << "INSERTING: " << tokens[2] << "\t" << tokens[0] << "\t" << tokens[1] << endl;
// insert a new employee object into bstree
tree.Insert(new employee(first_name, last_name, ID));
}
cout << endl << endl << "All employee records have been inserted to database successfully." << endl << endl;
// close file
file_reader.close();
}
这是我的binary search tree
(bstree):
#include <iomanip>
#include <iostream>
#include "bstree.h"
//--------------------------------------------
// Function: bstree()
// Purpose: Class constructor.
//--------------------------------------------
bstree::bstree()
{
count = 0;
root = NULL;
}
//--------------------------------------------
// Function: ~bstree()
// Purpose: Class destructor.
//--------------------------------------------
bstree::~bstree()
{
ClearTree(root);
return;
}
//--------------------------------------------
// Function: ClearTree()
// Purpose: Perform a recursive traversal of
// a tree destroying all nodes.
//--------------------------------------------
void bstree::ClearTree(employee *T)
{
if(T==NULL) return; // Nothing to clear
if(T->left != NULL) ClearTree(T->left); // Clear left sub-tree
if(T->right != NULL) ClearTree(T->right); // Clear right sub-tree
delete T; // Destroy this node
return;
}
//--------------------------------------------
// Function: isEmpty()
// Purpose: Return TRUE if tree is empty.
//--------------------------------------------
bool bstree::isEmpty()
{
return(root == NULL);
}
//--------------------------------------------
// Function: DupNode()
// Purpose: Duplicate a node in the tree. This
// is used to allow returning a complete
// structure from the tree without giving
// access into the tree through the pointers.
// Preconditions: None
// Returns: Pointer to a duplicate of the node arg
//--------------------------------------------
employee *bstree::DupNode(employee * T)
{
employee *dupNode;
dupNode = new employee();
*dupNode = *T; // Copy the data structure
dupNode->left = NULL; // Set the pointers to NULL
dupNode->right = NULL;
return dupNode;
}
//--------------------------------------------
// Function: SearchTree()
// Purpose: Perform an iterative search of the tree and
// return a pointer to a treenode containing the
// search key or NULL if not found.
// Preconditions: None
// Returns: Pointer to a duplicate of the node found
//--------------------------------------------
employee *bstree::SearchTree(unsigned int Key)
{
employee *temp;
temp = root;
while((temp != NULL) && (temp->ID != Key))
{
if(Key < temp->ID)
temp = temp->left; // Search key comes before this node.
else
temp = temp->right; // Search key comes after this node
}
if(temp == NULL)
return temp; // Search key not found
else
return(DupNode(temp)); // Found it so return a duplicate
}
//--------------------------------------------
// Function: Insert()
// Insert a new node into the tree.
// Preconditions: None
// Returns: int (TRUE if successful, FALSE otherwise)
//--------------------------------------------
bool bstree::Insert(employee *newNode)
{
employee *temp;
employee *back;
temp = root;
back = NULL;
while(temp != NULL) // Loop till temp falls out of the tree
{
back = temp;
if(newNode->ID < temp->ID)
temp = temp->left;
else if (newNode->ID > temp->ID)
temp = temp->right;
else
return false;
}
// Now attach the new node to the node that back points to
if(back == NULL) // Attach as root node in a new tree
root = newNode;
else
{
if(newNode->ID < back->ID)
back->left = newNode;
else if (newNode->ID > back->ID)
back->right = newNode;
else
return false;
}
return true;
}
//--------------------------------------------
// Function: Insert()
// Insert a new node into the tree.
// Preconditions: None
// Returns: int (TRUE if successful, FALSE otherwise)
//--------------------------------------------
bool bstree::Insert(unsigned int Key, string first_name, string last_name)
{
employee *newNode;
// Create the new node and copy data into it
newNode = new employee();
newNode->ID = Key;
newNode->first_name = first_name;
newNode->last_name = last_name;
newNode->left = newNode->right = NULL;
// Call other Insert() to do the actual insertion
return(Insert(newNode));
}
//--------------------------------------------
// Function: Delete()
// Purpose: Delete a node from the tree.
// Preconditions: Tree contains the node to delete
// Returns: int (TRUE if successful, FALSE otherwise)
//--------------------------------------------
bool bstree::Delete(unsigned int Key)
{
employee *back;
employee *temp;
employee *delParent; // Parent of node to delete
employee *delNode; // Node to delete
temp = root;
back = NULL;
// Find the node to delete
while((temp != NULL) && (Key != temp->ID))
{
back = temp;
if(Key < temp->ID)
temp = temp->left;
else
temp = temp->right;
}
if(temp == NULL) // Didn't find the one to delete
return false;
else
{
if(temp == root) // Deleting the root
{
delNode = root;
delParent = NULL;
}
else
{
delNode = temp;
delParent = back;
}
}
// Case 1: Deleting node with no children or one child
if(delNode->right == NULL)
{
if(delParent == NULL) // If deleting the root
{
root = delNode->left;
delete delNode;
return true;
}
else
{
if(delParent->left == delNode)
delParent->left = delNode->left;
else
delParent->right = delNode->left;
delete delNode;
return true;
}
}
else // There is at least one child
{
if(delNode->left == NULL) // Only 1 child and it is on the right
{
if(delParent == NULL) // If deleting the root
{
root = delNode->right;
delete delNode;
return true;
}
else
{
if(delParent->left == delNode)
delParent->left = delNode->right;
else
delParent->right = delNode->right;
delete delNode;
return true;
}
}
else // Case 2: Deleting node with two children
{
// Find the replacement value. Locate the node
// containing the largest value smaller than the
// key of the node being deleted.
temp = delNode->left;
back = delNode;
while(temp->right != NULL)
{
back = temp;
temp = temp->right;
}
// Copy the replacement values into the node to be deleted
delNode->ID = temp->ID;
delNode->first_name = temp->first_name;
delNode->last_name = temp->last_name;
// Remove the replacement node from the tree
if(back == delNode)
back->left = temp->left;
else
back->right = temp->left;
delete temp;
return true;
}
}
}
//--------------------------------------------
// Function: PrintOne()
// Purpose: Print data in one node of a tree.
// Preconditions: None
// Returns: void
//--------------------------------------------
void bstree::PrintOne(employee *T)
{
cout << T->ID << "\t\t" << T->first_name << "\t\t" << T->last_name << endl;
}
//--------------------------------------------
// Function: PrintAll()
// Purpose: Print the tree using a recursive
// traversal
// Preconditions: None
// Returns: void
//--------------------------------------------
void bstree::PrintAll(employee *T)
{
if(T != NULL)
{
PrintAll(T->left);
PrintOne(T);
PrintAll(T->right);
}
}
//--------------------------------------------
// Function: PrintTree()
// Purpose: Print the tree using a recursive
// traversal. This gives the user access
// to PrintAll() without giving access to
// the root of the tree.
// Preconditions: None
// Returns: void
//--------------------------------------------
void bstree::PrintTree()
{
PrintAll(root);
}
void bstree::saveToFile(const char * fileName)
{
ofstream file_writer;
file_writer.open(fileName);
saveToFile(file_writer, root);
file_writer.close();
}
void bstree::saveToFile(ofstream & file_writer, employee * T)
{
if (T != NULL)
{
saveToFile(file_writer, T->left);
file_writer << T->last_name;
file_writer << "\t";
file_writer << T->first_name;
file_writer << "\t";
file_writer << T->ID;
file_writer << "\n";
saveToFile(file_writer, T->right);
}
}
employee
构造函数均未将左指针或右指针初始化为NULL。对于复制构造函数而言,这尤其令人担忧,但是参数化构造函数真正可以显示痛苦的地方:
从文件加载时,请执行以下操作:
tree.Insert(new employee(first_name, last_name, ID));
触发此构造函数:
employee(std::string first_name, std::string last_name, unsigned int ID)
{
this->first_name = first_name;
this->last_name = last_name;
this->ID = ID;
}
上面没有任何地方分配给任何对象的左右成员指针。因此它们是不确定的,因此是垃圾。所以当你这样做时:
bool bstree::Insert(employee *newNode)
{
employee *temp;
employee *back;
temp = root;
back = NULL;
while(temp != NULL) // Loop till temp falls out of the tree
{
back = temp;
if(newNode->ID < temp->ID)
temp = temp->left;
else if (newNode->ID > temp->ID)
temp = temp->right;
else
return false;
}
// Now attach the new node to the node that back points to
if(back == NULL) // Attach as root node in a new tree
root = newNode;
else
{
if(newNode->ID < back->ID)
back->left = newNode;
else if (newNode->ID > back->ID)
back->right = newNode;
else
return false;
}
return true;
}
您正在追逐无效的指针,并且实际上,如果不调用未定义的行为,甚至无法对其进行更小的解引用评估。
这不会变成在线调试会话。您需要正确地初始化对象类的所有成员,最好在初始化器列表中:
class employee
{
public:
// note: this shouldn't even be *needed*
employee() : ID(), left(), right() {}
// parameterized constructor
employee(const std::string& first, const std::string& last, unsigned int id)
: first_name(first)
, last_name(last)
, ID(id)
, left(), right()
{
}
// copy-ctor. retains values; child pointers set as null
employee(const employee& obj)
: first_name(obj.first_name)
, last_name(obj.last_name)
, ID(obj.id)
, left(), right()
{
}
// assignment operator. does NOT copy child pointers
employee& operator =(const employee& obj)
{
first_name = obj.first_name;
last_name = obj.last_name;
ID = obj.ID;
}
std::string first_name;
std::string last_name;
unsigned int ID;
employee * left, * right;
};
通常,我会通过实现copy / swap习惯用法,对赋值运算符进行编码,以使用copy-constructor 。但是在这种情况下,这将是多余的,因为您的对象没有实际的动态管理成员(即,对象本身实际上是负责创建/销毁的成员)。
无论如何,以上是一个大问题,我没有花时间去分析除插入逻辑之外的实际树管理代码。如果Delete操作中潜伏着缺陷,对于二进制树总是很乏味的,我不会感到惊讶。但这足以使您走得更远。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句