文件无法打开,在调试器外运行导致seg fault(c ++)

时间:2010-03-14 10:51:31

标签: c++ segmentation-fault cstring ifstream

您好(并提前致谢)

我有点夸张,我似乎无法弄清楚为什么我会发生错误。

几点说明:

  1. 这是一门课程 - 可悲的是我 要求使用C字符串 而不是std :: string。
  2. 请不要修复我的代码(我不会那样学习,我会继续烦你)。 请指出我的逻辑中的缺陷并提出不同的功能/方式。
  3. 平台:Suse上的gcc版本4.4.1 Linux 11.2(2.6.31内核)
  4. 这是代码

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

3 个答案:

答案 0 :(得分: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分配一个char而不是char数组。与上述相同的解决方案。

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

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

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

其他担忧:

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

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

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

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

你在课堂上做了太多的记忆管理 我已经可以看到几个内存泄漏(如果我看起来更难,我相信我会发现指向超出范围的非现有对象的指针)。

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

  • 用std :: string替换“const char *”和“char *”。
  • 用std :: vector&lt;&gt;
  • 替换手动创建的数组
  • 喔。我们已经有了一个标准的词典。查找std :: map。

如果你停止尝试进行手动内存管理,我会打赌你的大多数问题都会消失,就像这是一个C程序。

词典:: Dectionary

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

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

答案 1 :(得分:0)

您为用户输入分配单个字符,而它可能要大得多。请改用char *userEntry = new char[MAX_PATH]

答案 2 :(得分:0)

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

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

我希望这个分配的目的是为了说明为什么我们在现代C ++中避免使用new [] / delete []和同类。如果这是一门Java课程,那就像在AWT 1.0中编写一些东西,只是为了证明它为什么在几年前被替换了。事情是,许多教师都是教师,因为他们可以宣称已经使用C ++“超过15年”。更多的,往往不是伤心地说,这意味着“我写C ++就好像它是1995年。” 为优秀的初学者选择“Accelerated C ++”到使用这些库的C ++。