Hello (and thanks in advance)
I'm in a bit of a quandry, I cant seem to figure out why I'm seg faulting.
A couple of notes:
- It's for a course -- and sadly I am required to use use C-strings instead of std::string.
- Please dont fix my code (I wont learn that way and I will keep bugging you). please just point out the flaws in my logic and suggest a different function/way.
- platform: gcc version 4.4.1 on Suse Linux 11.2 (2.6.31 kernel)
Here's the code
main.cpp:
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C/C++ Std Library)
#include <cstdlib> /// EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> /// cin, cout, ifstream
#include <cassert> /// assert
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "dict.h" /// Header for the dictionary class
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR CONSTANTS
#define ENTER '\n' /// Used to accept new lines, quit program.
#define SPACE ' ' /// One way to end the program
// ///////////////////////////////////////////////////////////////////////////////////
// CUSTOM DATA TYPES
/// File Namespace -- keep it local
namespace
{
/// Possible program prompts to display for the user.
enum FNS_Prompts
{
fileName_, /// prints out the name of the file
noFile_, /// no file was passed to the program
tooMany_, /// more than one file was passed to the program
noMemory_, /// Not enough memory to use the program
usage_, /// how to use the program
word_, /// ask the user to define a word.
notFound_, /// the word is not in the dictionary
done_, /// the program is closing normally
};
}
// ///////////////////////////////////////////////////////////////////////////////////
// Namespace
using namespace std; /// Nothing special in the way of namespaces
// ///////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS
/** prompt() prompts the user to do something, uses enum Prompts for parameter.
*/
void prompt(FNS_Prompts msg /** determines the prompt to use*/)
{
switch(msg)
{
case fileName_ :
{
cout << ENTER << ENTER << "The file name is: ";
break;
}
case noFile_ :
{
cout << ENTER << ENTER << "...Sorry, a dictionary file is needed. Try again." << endl;
break;
}
case tooMany_ :
{
cout << ENTER << ENTER << "...Sorry, you can only specify one dictionary file. Try again." << endl;
break;
}
case noMemory_ :
{
cout << ENTER << ENTER << "...Sorry, there isn't enough memory available to run this program." << endl;
break;
}
case usage_ :
{
cout << "USAGE:" << endl
<< " lookup.exe [dictionary file name]" << endl << endl;
break;
}
case done_ :
{
cout << ENTER << ENTER << "like Master P says, \"Word.\"" << ENTER << endl;
break;
}
case word_ :
{
cout << ENTER << ENTER << "Enter a word in the dictionary to get it's definition." << ENTER
<< "Enter \"?\" to get a sorted list of all words in the dictionary." << ENTER
<< "... Press the Enter key to quit the program: ";
break;
}
case notFound_ :
{
cout << ENTER << ENTER << "...Sorry, that word is not in the dictionary." << endl;
break;
}
default :
{
cout << ENTER << ENTER << "something passed an invalid enum to prompt(). " << endl;
assert(false); /// something passed in an invalid enum
}
}
}
/** useDictionary() uses the dictionary created by createDictionary
* - prompts user to lookup a word
* - ends when the user enters an empty word
*/
void useDictionary(Dictionary &d)
{
char *userEntry = new char; /// user's input on the command line
if( !userEntry ) // check the pointer to the heap
{ cout << ENTER << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
do
{
prompt(word_);
// test code
cout << endl << "----------------------------------------" << endl
<< "Enter something: ";
cin.getline(userEntry, INPUT_LINE_MAX_LEN, ENTER);
cout << ENTER << userEntry << endl;
}while ( userEntry[0] != NIL && userEntry[0] != SPACE );
// GARBAGE COLLECTION
delete[] userEntry;
}
/** Program Entry
* Reads in the required, single file from the command prompt.
* - If there is no file, state such and error out.
* - If there is more than one file, state such and error out.
* - If there is a single file:
* - Create the database object
* - Populate the database object
* - Prompt the user for entry
* main() will return EXIT_SUCCESS upon termination.
*/
int main(int argc, /// the number of files being passed into the program
char *argv[] /// pointer to the filename being passed into tthe program
)
{
// EXECUTE
/* Testing code * /
char tempFile[INPUT_LINE_MAX_LEN] = {NIL};
cout << "enter filename: ";
cin.getline(tempFile, INPUT_LINE_MAX_LEN, '\n');
*/
// uncomment after successful debugging
if(argc <= 1)
{
prompt(noFile_); prompt(usage_);
return EXIT_FAILURE; /// no file was passed to the program
}
else if(argc > 2)
{
prompt(tooMany_); prompt(usage_);
return EXIT_FAILURE; /// more than one file was passed to the program
}
else
{
prompt(fileName_); cout << argv[1]; // print out name of dictionary file
if( !argv[1] )
{
prompt(noFile_); prompt(usage_);
return EXIT_FAILURE; /// file does not exist
}
/*
file.open( argv[1] ); // open file
numEntries >> in.getline(file); // determine number of dictionary objects to create
file.close(); // close file
Dictionary[ numEntries ](argv[1]); // create the dictionary object
*/
// TEMPORARY FILE FOR TESTING!!!!
//Dictionary scrabble(tempFile);
Dictionary scrabble(argv[1]); // creaate the dicitonary object
//*/
useDictionary(scrabble); // prompt the user, use the dictionary
}
// exit
return EXIT_SUCCESS; /// terminate program.
}
Dict.h/.cpp
#ifndef DICT_H
#define DICT_H
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (Custom header files)
#include "entry.h" /// class for dictionary entries
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR MACROS
#define INPUT_LINE_MAX_LEN 256 /// Maximum length of each line in the dictionary file
class Dictionary
{
public :
//
// Do NOT modify the public section of this class
//
typedef void (*WordDefFunc)(const char *word, const char *definition);
Dictionary( const char *filename );
~Dictionary();
const char *lookupDefinition( const char *word );
void forEach( WordDefFunc func );
private :
//
// You get to provide the private members
//
// VARIABLES
int m_numEntries; /// stores the number of entries in the dictionary
Entry *m_DictEntry_ptr; /// points to an array of class Entry
// Private Functions
};
#endif
-----------------------------------
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C/C++ Std Library)
#include <iostream> /// cout, getline
#include <fstream> // ifstream
#include <cstring> /// strchr
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "dict.h" /// Header file required by assignment
//#include "entry.h" /// Dicitonary Entry Class
// //////////////////////////////////////////////////////开发者_Go百科/////////////////////////////
// PRE-PROCESSOR MACROS
#define COMMA ',' /// Delimiter for file
#define ENTER '\n' /// Carriage return character
#define FILE_ERR_MSG "The data file could not be opened. Program will now terminate."
#pragma warning(disable : 4996) /// turn off MS compiler warning about strcpy()
// ///////////////////////////////////////////////////////////////////////////////////
// Namespace reference
using namespace std;
// ///////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBER FUNCTIONS
/**
* Sorts the dictionary entries.
*/
/*
static void sortDictionary(?)
{
// sort through the words using qsort
}
*/
/** NO LONGER NEEDED??
* parses out the length of the first cell in a delimited cell
* /
int getWordLength(char *str /// string of data to parse
)
{
return strcspn(str, COMMA);
}
*/
// ///////////////////////////////////////////////////////////////////////////////////
// PUBLIC MEMBER FUNCTIONS
/** constructor for the class
* - opens/reads in file
* - creates initializes the array of member vars
* - creates pointers to entry objects
* - stores pointers to entry objects in member var
* - ? sort now or later?
*/
Dictionary::Dictionary( const char *filename )
{
// Create a filestream, open the file to be read in
ifstream dataFile(filename, ios::in );
/*
if( dataFile.fail() )
{ cout << FILE_ERR_MSG << endl; exit(EXIT_FAILURE);
}
*/
if( dataFile.is_open() )
{
// read first line of data
// TEST CODE in.getline(dataFile, INPUT_LINE_MAX_LEN) >> m_numEntries;
// TEST CODE char temp[INPUT_LINE_MAX_LEN] = {NIL};
// TEST CODE dataFile.getline(temp,INPUT_LINE_MAX_LEN,'\n');
dataFile >> m_numEntries; /** Number of terms in the dictionary file
* \todo find out how many lines in the file, subtract one, ingore first line
*/
//create the array of entries
m_DictEntry_ptr = new Entry[m_numEntries];
// check for valid memory allocation
if( !m_DictEntry_ptr )
{ cout << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
// loop thru each line of the file, parsing words/def's and populating entry objects
for(int EntryIdx = 0; EntryIdx < m_numEntries; ++EntryIdx)
{
// VARIABLES
char *tempW_ptr; /// points to a temporary word
char *tempD_ptr; /// points to a temporary def
char *w_ptr; /// points to the word in the Entry object
char *d_ptr; /// points to the definition in the Entry
int tempWLen; /// length of the temp word string
int tempDLen; /// length of the temp def string
char tempLine[INPUT_LINE_MAX_LEN] = {NIL}; /// stores a single line from the file
// EXECUTE
// getline(dataFile, tempLine) // get a "word,def" line from the file
dataFile.getline(tempLine, INPUT_LINE_MAX_LEN); // get a "word,def" line from the file
// Parse the string
tempW_ptr = tempLine; // point the temp word pointer at the first char in the line
tempD_ptr = strchr(tempLine, COMMA); // point the def pointer at the comma
*tempD_ptr = NIL; // replace the comma with a NIL
++tempD_ptr; // increment the temp def pointer
// find the string lengths... +1 to account for terminator
tempWLen = strlen(tempW_ptr) + 1;
tempDLen = strlen(tempD_ptr) + 1;
// Allocate heap memory for the term and defnition
w_ptr = new char[ tempWLen ];
d_ptr = new char[ tempDLen ];
// check memory allocation
if( !w_ptr && !d_ptr )
{ cout << MEM_ERR_MSG << endl; exit(EXIT_FAILURE);
}
// copy the temp word, def into the newly allocated memory and terminate the strings
strcpy(w_ptr,tempW_ptr); w_ptr[tempWLen] = NIL;
strcpy(d_ptr,tempD_ptr); d_ptr[tempDLen] = NIL;
// set the pointers for the entry objects
m_DictEntry_ptr[ EntryIdx ].setWordPtr(w_ptr);
m_DictEntry_ptr[ EntryIdx ].setDefPtr(d_ptr);
}
// close the file
dataFile.close();
}
else
{ cout << ENTER << FILE_ERR_MSG << endl; exit(EXIT_FAILURE);
}
}
/**
* cleans up dynamic memory
*/
Dictionary::~Dictionary()
{
delete[] m_DictEntry_ptr; /// thou shalt not have memory leaks.
}
/**
* Looks up definition
*/
/*
const char *lookupDefinition( const char *word )
{
// print out the word ---- definition
}
*/
/**
* prints out the entire dictionary in sorted order
*/
/*
void forEach( WordDefFunc func )
{
// to sort before or now.... that is the question
}
*/
Entry.h/cpp
#ifndef ENTRY_H
#define ENTRY_H
// ///////////////////////////////////////////////////////////////////////////////////
// INCLUDES (C++ Std lib)
#include <cstdlib> /// EXIT_SUCCESS, NULL
// ///////////////////////////////////////////////////////////////////////////////////
// PRE-PROCESSOR MACROS
#define NIL '\0' /// C-String terminator
#define MEM_ERR_MSG "Memory allocation has failed. Program will now terminate."
// ///////////////////////////////////////////////////////////////////////////////////
// CLASS DEFINITION
class Entry
{
public:
Entry(void) : m_word_ptr(NULL), m_def_ptr(NULL) { /* default constructor */ };
void setWordPtr(char *w_ptr); /// sets the pointer to the word - only if the pointer is empty
void setDefPtr(char *d_ptr); /// sets the ponter to the definition - only if the pointer is empty
/// returns what is pointed to by the word pointer
char getWord(void) const { return *m_word_ptr; }
/// returns what is pointed to by the definition pointer
char getDef(void) const { return *m_def_ptr; }
private:
char *m_word_ptr; /** points to a dictionary word */
char *m_def_ptr; /** points to a dictionary definition */
};
#endif
--------------------------------------------------
// ///////////////////////////////////////////////////////////////////////////////////
// DEPENDENCIES (custom header files)
#include "entry.h" /// class header file
// ///////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
/*
* only change the word member var if it is in its initial state
*/
void Entry::setWordPtr(char *w_ptr)
{
if(m_word_ptr == NULL)
{ m_word_ptr = w_ptr;
}
}
/*
* only change the def member var if it is in its initial state
*/
void Entry::setDefPtr(char *d_ptr)
{
if(m_def_ptr == NULL)
{ m_word_ptr = d_ptr;
}
}
Easy Problems
The easy problem to spot is here:
cin.getline(userEntry, INPUT_LINE_MAX_LEN, ENTER);
The first problem is that unless INPUT_LINE_MAX_LEN == 1 then the line is wrong.
This is easy to fix change the declaration og userEntry to:
userEntry = new char[INPUT_LINE_MAX_LEN + 1];
The second problem I see is:
// GARBAGE COLLECTION
delete[] userEntry;
Which is wrong because you only allocate a single char not an array of char for userEntry. The same solution as above.
A better solution is to use a managed array of characters (i.e. a string)
std::string userEntry;
std::getline(std::cin,userEntry);
No need to allocate or delete and it re-sizes as required so you can't write off then end.
Other worries:
Seeing this in C++ code makes the hares on the back of my nech stand up:
typedef void (*WordDefFunc)(const char *word, const char *definition);
In C++ it is much easier to use interfaces rather than function pointers. (Yes I know C++ does not have the keyword interface. But the concept of an interface is the same in all languages it is a class definition that implements a specific contract).
class IFuncAction
{
virtual void action(char const* word,char const* definition) = 0;
};
You do way too much memory management in the class.
I can already see several memory leaks (if I look harder I am sure I will spot pointers to non existant objects that have gone out of scope).
A modern C++ program will have very few pointers in it.
- Replace your "const char*" and your "char*" with std::string.
- Replace your manually created arrays with std::vector<>
- Oh. We already have a standard Dictionary. Look up std::map.
I will bet most of your problems will disapear if you stop trying to do manual memory management like this was a C program.
Dictionary::Dectionary
Ideally the dictionary constructor woudl look like this:
// Pass a stream to the constructor.
// Now a dictionary can be created from a file or
// a string (stringstream) when doing unit tests.
//
Dictionary::Dictionary(std::istream& data)
{
if (!data)
{ throw MyProblemException("Bad Input to Dictionary constructor");
}
Entry item;
// While we can read data from the stream
while(data >> item)
{
// Add it to the store
m_DictEntry.push_back(item);
}
}
std::istream& operator>>(std::istream& str,Entry& data)
{
std::getline(str,data.m_word, COMMA);
std::getline(str,data.m_definition);
return str;
}
The previous post picked out the likely immedate cause of the segfault, you need INPUT_LINE_MAX_LEN+1 bytes to store the input. But in this function there is no reason nto new/delete in the first place, just use char userEntry[INPUT_LINE_MAX_LEN+1]; on the stack
Also note that you do have a memory leaks -- the Entry class owns the pointers, but does not delete them. Since you are not copying anything, then just adding the delete statements to this class will work.
I hope that the intent of this assigment is to illustrate just why we avoid new[]/delete[] and the ilk in modern C++. If this were a Java course, this would be like programming something in AWT 1.0 just to demonstrate why it was replaced years ago. The things is, that many instructors are instructors because they can profess to have used C++ "for 15+ years." More often than not, sad to say, this means "I write C++ as if it were 1995." Pick up "Accelerated C++" for a good beginners into to C++ that uses the libraries.
You are allocation single character for user entry, while it could be a lot bigger. Use char *userEntry = new char[MAX_PATH]
instead
精彩评论