1

数週間前、私は初めて (私はそれらを使用するのに慣れていません) floatsdoublesを使用していましたが、比較オペランドにいくつかの問題があります。その型に値を割り当てようとしているときにも問題がありましたが、それも解決しました...

今日、私は C++ でライブラリを作成していて、エラーを見つけました... うーん... 変ですか? それとも私の愚かな考えですか?

これはコードです:

ini::ini(const char * path, bool _autoflush_ /*= false*/) {
/* Storing file name ... */
f_name = new char[strlen(path)+1];
strcpy(f_name, path);

/* Storing autoflush ... */
autoflush = _autoflush_;

/* First step: getting file size */
    /* Open the file in read/append mode */
    FILE * fd = fopen(path, "r");
    /* On non-valid descriptor, goto next step directly */
    if(fd == NULL) f_size = 1; goto allocbuffer;

    /* Seek to the end */
    fseek(fd, 0, SEEK_END);
    /* Get file size */
    f_size = (unsigned long int)ftell(fd) + 1;

/* Second step: allocating memory for the buffer */ allocbuffer:
    cout << endl << endl << endl << endl << "Wanting " << sizeof(char)*f_size << " bytes of memory!" << endl << endl << endl << endl;
    /* Allocate buffer-space */
    buffer = (char*)malloc(sizeof(char)*f_size);
    if(buffer == NULL) {
        errord = (char*)malloc(strlen(INI_ERROR_NOT_ENOUGH_MEMORY) + 1);
        strcpy(errord, INI_ERROR_NOT_ENOUGH_MEMORY);
        cout << endl << endl << endl << endl << "Last error: \"" << errord << "\"." << endl << endl << endl << endl;
        return;
    }
    /* Initialize and fill it with null bytes */
    memset(buffer, 0, f_size);
    /* Goto next step */
    if(fd == NULL) goto endconstruct;

/* Third step: storing in the buffer */ loadbuffer:
    /* Rewind file descriptor */
    rewind(fd);
    /* Read from file */
    if(fread(buffer, 1, f_size, fd) != f_size) {
        errord = (char*)malloc(strlen(INI_ERROR_NOT_READED) + 1);
        strcpy(errord, INI_ERROR_NOT_READED);
        cout << endl << endl << endl << endl << "Last error: \"" << errord << "\"." << endl << endl << endl << endl;
        cout << endl << endl << endl << endl << "BYTES OF FILE: \"" << f_size << "\"." << endl << endl << endl << endl;
    }
    /* Close file descriptor */
    fclose(fd);
    /* Get number of lines */
    f_line = strnum(buffer, "\n") + 1;

/* End building of object */
endconstruct:
    /* Print out what is stored in the buffer NOW */
    cout << endl << endl << endl << endl << "Buffer is:" << endl << buffer << endl << endl << endl << endl;

return;

}

おそらくiniライブラリはすでに作成されており、私のものよりもはるかに優れています. しかし、私は C から C++ を学び始めており、何か面白くて便利なことで練習したいと思っています。クラス宣言を見逃していました。ここに貼り付ける必要があるかどうかはわかりませんが、次のとおりです。

    /** @def INI_ERROR_NOT_READED
    @brief <em>Not readed as many bytes as required</em>
*/
#define INI_ERROR_NOT_READED "Not readed as many bytes as required"
/** @def INI_ERROR_NOT_ENOUGH_MEMORY
    @brief <em>There is not enough memory</em>
*/
#define INI_ERROR_NOT_ENOUGH_MEMORY "There is not enough memory"

/** @class ini
    @brief Class to describe <em>ini</em> files.

    It describes an ini file. All the file is readed and loaded
    in memory, for faster access. This class is the
    improved & C++ version of the old, monstruous
    functions defined in the old, monstruous IO Utilities
    Library. Writting functions use dynamic memory reallocation
    and file flush to the filesystem.
*/
class ini {
    public:
        /** @brief Constructor. Gives initial memory for the buffer and loads all the file in that buffer.
         * 
         * @param path - Path of the <em>ini</em> file to open.
         * @param _autoflush_ - Whether to auto-flush changes to hard disk or not.
         *  If you don't set it to any value, <em>false</em> is taked as default
         *  value and you have to flush changes manually using member function flush().
         *  Setting it to <em>true</em> may make it less efficient, so be careful
         *  if you're going to make a lot of changes in the <em>ini</em> file.
        */
        ini                     (const char * path, bool _autoflush_ = false);
        /** @brief Destructor. Frees the memory pointed by <em>buffer</em> and destroys the #ini object.
         * 
         * It's very important to free the memory buffer, to avoid memory corruptions.
        */
        ~ini                    (void);
        /** @brief Gets last error stored in private member <em>errord</em>.
         * 
         * @return Last error-descriptor as string.
        */
        char *      geterror    (void);
        /** @brief Flush changes made in the buffer to the hard disk.
         * 
         * You can do it manually or set auto-flushing by the second argument of
         * ini::ini().
         * 
         * @par Example of usage:
         * @code
         *  ini inid("myini.ini");
         *  // make changes
         *  inid.flush();
         * @endcode
        */
        void        flush       (void);
        /** @brief Flush changes made in the buffer to *another* file the hard disk.
         * 
         * Using this function instead of normal flush(void), you are able to
         * save the buffer to another #ini file that is not the original one.
         * 
         * @par Example of usage:
         * @code
         *  ini inid("myini.ini");
             *  // make changes
         *  inid.flush("myini.backup.ini");
         * @endcode
        */
        void        flush       (const char * path);
        /** @brief Checks if a section exists.
         * 
         * @param tsection - The name of the section to check, without the braces.
         * 
         * @return #true if the section exists; #false if not.
        */
        bool        sectExists  (const char * tsection);
        /** @brief Gets the line in that a section starts.
         * 
         * @param tsection - The name of the section to check, without the braces.
         * 
         * @return The line in that the section starts; -1 if not-founded section.
         *  Keep in mind that the first line is 1, the second, 2,...
        */
        int         sectStart   (const char * tsection);
        /** @brief Gets the line in that a section ends.
         * 
         * @param tsection - The name of the section to check, without the braces.
         * 
         * @return The line in that the section ends; -1 if not-founded section.
         *  Keep in mind that the first line is 1, the second, 2,...
        */
        int         sectStop    (const char * tsection);
        /** @brief Checks if a key exists.
         * 
         * @param tsection - The name of the section to check, without the braces.
         *  If the key is outside any section (if it's a #KWOS), then <em>tsection</em>
         *  should be #KWOS.
         * @param tkey - The name of the key to check.
         * 
         * @return #true if the key exists in the specified section; #false if not.
        */
        int         keyExists   (const char * tsection, const char * tkey);
        /** @brief Reads the value of a key as a string.
         * 
         * @param tsection - The name of the section to read from, without the braces.
         *  If the key is outside any section (if it's a #KWOS), then <em>tsection</em>
         *  should be #KWOS.
         * @param tkey - The name of the key to read its value.
         * @param tval - The default string to return if cannot found the key.
         * 
         * @return The value of the key <em>tkey</em> in section <em>tsection</em>; or
         *  <em>tval</em> when non-existing key.
        */
        char *      read        (const char * tsection, const char * tkey, const char * tval);
        /** @brief Reads the value of a key as an integer value.
         * 
         * @param tsection - The name of the section to read from, without the braces.
         *  If the key is outside any section (if it's a #KWOS), then <em>tsection</em>
         *  should be #KWOS.
         * @param tkey - The name of the key to read its value.
         * @param tval - The default value to return if cannot found the key.
         * 
         * @return The value of the key <em>tkey</em> in section <em>tsection</em>; or
         *  <em>tval</em> when non-existing key.
        */
        long int    readi       (const char * tsection, const char * tkey, int tval);
        bool        delKey      (const char * tsection, const char * tkey);
        bool        delSect     (const char * tsection);
        bool        write       (const char * tsection, const char * tkey, const char * tval);
        bool        write       (const char * tsection, const char * tkey, int tval);
    private:
        unsigned long int       f_size; /**< File size. */
        unsigned int            f_line; /**< Number of lines of the <em>ini</em> file. */
        char *                  buffer; /**< Memory buffer to store data. Dynamimcally reallocated. */
        char *                  f_name; /**< File name. */
        bool                 autoflush; /**< Whether to auto-flush to hard disk or not. */
        char *                  errord; /**< Last error stored internally by the functions of the #ini class. */
};

いくつかの「テスト」の後、問題が「f_size」変数にあることが最終的にわかりました。なんで?わからない。しかし、標準出力に出力すると、非常に大きな数値が表示されます。これは、メモリ割り当て (malloc を使用) エラーと、それに続くファイルからの読み取り (または memset を使用した初期化) 時のエラーの問題です。

助けていただければ幸いです。そして、エラーを確認して学習を続けるためのリンク、参考文献、または説明。

ありがとう!

PS: Linux Debian の "squeeze"、amd64 で g++ を使用しています。

4

3 に答える 3

3

ここに、微妙ではあるが重要な問題が 1 つあります。

 if(fd == NULL) f_size = 1; goto allocbuffer;

ファイル存在する場合でも、ロジックは label にジャンプしallocbufferます。

これを修正するには、ブレースを使用します。クリスタルの透明度のためにインデントします。

 if(fd == NULL)
 {
      f_size = 1;
      goto allocbuffer;
 }

出口までスキップすると、変数が正しく初期化されません。

于 2011-12-25T17:43:28.530 に答える
2

wallyk によって指摘された問題に加えてftell、エラーが発生すると -1 が返され、それを unsigned long にキャストするとオーバーフローが発生します。

いずれにせよ、ftell を使用してファイルのサイズを取得しないでください。使用fstatまたは類似。

于 2011-12-25T17:46:06.033 に答える
0

コードのデバッグは行いませんが (これは codereview.se に属します)、ヒントをいくつか示します。

f_name = new char[strlen(path)+1];
strcpy(f_name, path);

代わりに: f_name を次のようstd::stringに定義し、...

f_name = path;

        errord = (char*)malloc(strlen(INI_ERROR_NOT_READED) + 1);
        strcpy(errord, INI_ERROR_NOT_READED);
        cout << "Last error: \"" << errord << "\"." << endl;

画面に文字列を書き込むためだけにメモリを動的に割り当てていますか? 代わりに、次のようにします。

        errord = INI_ERROR_NOT_READED;
        cout << "Last error: \"" << errord << "\"." << endl;

また、ファイル全体をバッファーに読み込むには、もっと簡単な方法があります (数行のコードなど)。https://stackoverflow.com/a/2602060/399317を参照してください。


簡単に言えば、独自のコンテナを作成していない限り、あなたの友達new[]ではありません。malloc代わりに STL コンテナーを使用します (std::vectorたとえば、これstd::stringも役立ちます)。

于 2011-12-25T17:51:55.947 に答える