“unsigned long int”和“unsigned long long int”分配问题
几周前,我第一次使用(我不习惯使用它们)浮点、双精度,并且我在比较操作数方面遇到了一些问题。我在尝试为该类型赋值时也遇到了问题,但我也解决了它...
今天,我正在用 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++。
A few weeks ago, I was using for first time (I'm not used to use them) floats, doubles, and I have some problems with the comparison operand. I also have had problems while trying to assign values to that types, but I've solved it as well...
Today, I'm making a library in C++, and I have found an error... well... strange? or just my stupid thinking?
This is the code:
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;
}
Probably ini libraries are already created, and much better than my. But I'm starting learning C++ from C, and I want to practice with something interesting and useful. I missed the class declaration, I don't know if it's necessary to paste it here, but here is:
/** @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. */
};
After a few 'tests', I have finally discovered that the problem is in the 'f_size' variable. Why? Don't know. But, if I print it out to stdout, it displays a very very big number. That's the problem of the memory-assignment (with malloc) error and the subsequent error at reading from file (or initialization with memset).
Help is much appreciated. And links, refs or explanations to have me see my error, and continue learning.
Thanks!
P.S.: I'm using g++ in a Linux Debian "squeeze", amd64.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这里有一个微妙但重要的问题:
如果文件确实存在,逻辑仍然跳转到标签
allocbuffer
。要解决此问题,请使用大括号。并缩进以获得水晶般的清晰度。
通过跳到退出,变量未正确初始化。
Here is one subtle but significant issue:
If the file does exist, the logic still jumps to label
allocbuffer
.To fix this, use braces. And indent for crystal clarity.
By skipping to the exit, variables are not initialized properly.
除了 wallyk 指出的问题之外,当
ftell
遇到错误时,它会返回 -1 并将其转换为 unsigned long 会导致溢出。无论如何,不要使用 ftell 来获取文件的大小。使用
fstat
或类似的。In addition to the issue pointed out by wallyk, when
ftell
encounters an error it returns -1 and casting that to an unsigned long is causing an overflow.In any case, don't use ftell to get a file's size. Use
fstat
or similar.我不会为您调试代码(这更多属于 codereview.se),但这里有一些提示:
相反:将 f_name 定义为
std::string
并执行...是吗?就像,动态分配内存只是为了将字符串写入屏幕?
相反,只需简单地操作:
还将整个文件读入缓冲区,还有更简单的方法(例如几行代码)。请参阅https://stackoverflow.com/a/2602060/399317。
长话短说:
new[]
和malloc
不是你的朋友,除非你正在编写自己的容器。请改用 STL 容器(例如std::vector
,std::string
也很有帮助)。I won't debug the code for you (this more belongs to codereview.se), but here are some hints:
Instead: define f_name as
std::string
and do a...Are you, like, dynamically allocating memory just to write a string into the screen?
Instead do simply:
Also to read a whole file into a buffer, there are simpler ways (as in, a few lines of code). See https://stackoverflow.com/a/2602060/399317 .
Long story short:
new[]
andmalloc
aren't your friends unless you're writing your own containers. Use STL containers instead (std::vector
for instance,std::string
is helpful too).