【问题标题】:'unsigned long int', and 'unsigned long long int' assignment issue“unsigned long int”和“unsigned long long int”分配问题
【发布时间】:2012-01-27 16:13:45
【问题描述】:

几周前,我第一次使用(我不习惯使用它们)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 初始化)时的后续错误的问题。

非常感谢您的帮助。以及链接、参考或解释,让我看到我的错误并继续学习。

谢谢!

P.S.:我在 Linux Debian“挤压”中使用 g++,amd64。

【问题讨论】:

  • 顺便说一句 strlennew char[]?甚至malloc?你还在用 C 语言思考,使用 std::string 和繁荣
  • 但是我看到的很多函数“仍然”使用 const char*char*... 我需要 realloc 为缓冲区动态重新分配内存。
  • 如果您不编写容器类,为什么需要为缓冲区动态重新分配内存?
  • ftell 返回一个 signed long int。由于ftell 在错误时返回-1,因此您的转换可能非常显着。 +1 在这条线上也很可疑。 goto allocbuffer; 在 C++ 中确实应该避免使用。这在 C 中是可以的(虽然不确定是否在这种情况下),但是在 C++ 中要正确要困难得多。 (因此,您仍然在 C 方面考虑太多 - 用一段更简单的代码很多缩小您的问题范围,您可能会得到更好的建议。)
  • 如果您使用 std::stringstd::ifstream,则不需要 realloc。我建议你学习使用它们,因为它会让你的内存管理更容易。

标签: c++ g++ unsigned-integer unsigned-long-long-int


【解决方案1】:

这是一个微妙但重要的问题:

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

如果文件确实存在,逻辑仍然跳转到标签allocbuffer

要解决此问题,请使用大括号。并缩进以获得水晶般的清晰度。

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

通过跳到出口,变量没有正确初始化。

【讨论】:

    【解决方案2】:

    除了 wallyk 指出的问题之外,当ftell 遇到错误时,它会返回 -1 并将其转换为 unsigned long 会导致溢出。

    无论如何,don't use ftell to get a file's size。使用 fstat 或类似名称。

    【讨论】:

      【解决方案3】:

      我不会为你调试代码(这更多属于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::vectorstd::string 也很有帮助)。

      【讨论】:

      • 谢谢!所以,我将使用 std::string 和...@Kos...'直接'分配'errord'?但是,然后我会得到一个段错误或类似的东西。指针是我过去许多问题的根源......并且'errord'是'ini'类的私有成员。在那种情况下,'cout' 只是为了在我遇到段错误之前打印出来......
      猜你喜欢
      • 2016-07-29
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 1970-01-01
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-08
      相关资源
      最近更新 更多