0

你好(提前感谢)

我有点困惑,我似乎无法弄清楚为什么我会出现段错误。

几点注意事项:

  1. 这是一门课程——遗憾的是,我需要使用 C-strings 而不是 std::string。
  2. 请不要修复我的代码(我不会那样学习,我会一直困扰你)。请指出我的逻辑中的缺陷并提出不同的功能/方式。
  3. 平台:Suse Linux 11.2(2.6.31 内核)上的 gcc 版本 4.4.1

这是代码

主.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.
}

字典.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


// ///////////////////////////////////////////////////////////////////////////////////
// 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
  }
*/

入口.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;
      }
}
4

3 回答 3

2

容易的问题

容易发现的问题在这里:

cin.getline(userEntry,  INPUT_LINE_MAX_LEN,  ENTER); 

第一个问题是除非 INPUT_LINE_MAX_LEN == 1 否则该行是错误的。
这很容易修复将声明 og userEntry 更改为:

userEntry = new char[INPUT_LINE_MAX_LEN + 1];

我看到的第二个问题是:

// GARBAGE COLLECTION   
delete[] userEntry; 

这是错误的,因为您只为 userEntry 分配了一个字符而不是一个字符数组。与上述相同的解决方案。

更好的解决方案是使用托管字符数组(即字符串)

std::string    userEntry;
std::getline(std::cin,userEntry);

无需分配或删除,它会根据需要重新调整大小,因此您无法注销然后结束。

其他担忧:

在 C++ 代码中看到这一点让我背后的野兔站起来:

typedef void  (*WordDefFunc)(const char  *word, const char  *definition); 

在 C++ 中,使用接口比使用函数指针要容易得多。(是的,我知道 C++ 没有关键字 interface。但是接口的概念在所有语言中都是相同的,它是实现特定契约的类定义)。

class IFuncAction
{
    virtual void action(char const* word,char const* definition) = 0;
};

你在课堂上做了太多的内存管理。
我已经可以看到几个内存泄漏(如果我仔细观察,我相信我会发现指向超出范围的不存在对象的指针)。

现代 C++ 程序中的指针很少。

  • 用 std::string 替换你的“const char*”和“char*”。
  • 用 std::vector<> 替换您手动创建的数组
  • 哦。我们已经有了一个标准的字典。查找 std::map。

我敢打赌,如果您停止尝试像 C 程序那样进行手动内存管理,那么您的大多数问题都会消失。

词典::词典

理想情况下,字典构造函数看起来像这样:

// 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;
} 
于 2010-03-14T16:40:00.360 回答
0

您正在为用户输入分配单个字符,而它可能会更大。char *userEntry = new char[MAX_PATH]改为使用

于 2010-03-14T11:01:00.413 回答
0

上一篇文章指出了段错误的可能直接原因,您需要 INPUT_LINE_MAX_LEN+1 个字节来存储输入。但是在这个函数中,首先没有理由要新建/删除,只需使用 char userEntry[INPUT_LINE_MAX_LEN+1]; 在堆栈上

另请注意,您确实存在内存泄漏 - Entry 类拥有指针,但不会删除它们。由于您没有复制任何内容,因此只需将删除语句添加到此类即可。

我希望这个分配的目的是说明为什么我们要避免使用 new[]/delete[] 和现代 C++ 中的同类。如果这是一门 Java 课程,这就像在 AWT 1.0 中编写一些东西只是为了说明为什么它在几年前就被取代了。事实是,许多讲师都是讲师,因为他们可以自称使用 C++“超过 15 年”。很多时候,遗憾的是,这意味着“我写 C++ 就好像它是 1995 年一样”。为使用库的 C++ 初学者挑选“加速 C++”。

于 2010-03-14T16:25:53.290 回答