_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) C++ 文件
Posted
技术标签:
【中文标题】_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) C++ 文件【英文标题】:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) C++ FILE 【发布时间】:2015-04-18 04:47:49 【问题描述】:我知道有几篇关于此错误的帖子,但它们是针对特定情况的。 我正在制作一个文件拆分/连接器,它具有以下要求: - 用户必须输入文件名/输入路径和输出文件夹。 我用将原始文件拆分为 N 部分(用户必须输入)的基本部分编写了拆分函数,一切都很好。 然后我对该函数进行了一些修改以满足“输出文件夹”的要求,然后当我运行程序时,出现了该错误(尽管已成功构建)。 有人可以解释/澄清我在代码中做错了什么吗?我仍然是处理文件/内存泄漏的初学者,因此感谢所有帮助/批评。
char *GetFileName(char *path)
char *filename = strrchr(path, '\\');
if (filename == NULL)
filename = path;
else
filename++;
return filename;
void split_F(const char* file_name, const char* output_folder, int number_of_part)
FILE *fp_read = fopen(file_name, "rb");
//calculate file size
int file_size;
fseek(fp_read, 0L, SEEK_END);
file_size = ftell(fp_read);
rewind(fp_read); //reset file pointer
//calculate number of parts
long size_of_part;
size_of_part = (int)ceil((double)file_size / number_of_part);
cout << "Total files after split: " << number_of_part << endl
<< "...Processing..." << endl;
//extract file name
char *first_part = new char[255];
char *temp = new char[255];
strcpy(temp, file_name);
first_part = GetFileName(temp);
cout << endl << "File name is: " << first_part;
//main process
char* name = new char[255];
strcpy(name, output_folder);
int bytesRemaining = file_size;
//create buffer
char *buffer = new char[size_of_part];
for (int count = 1; count <= number_of_part; count++)
sprintf(name, "%s.part_%03d", first_part, count); //attach file name to output directory
FILE *fp_write = fopen(name, "wb");
long partSize;
if (bytesRemaining > size_of_part)
partSize = size_of_part;
else
partSize = bytesRemaining;
fread(buffer, partSize, 1, fp_read);
fwrite(buffer, partSize, 1, fp_write);
cout << "> File: " << name << " done babe!" << endl;
fclose(fp_write);
fclose(fp_read);
delete[] buffer;
delete[] name;
delete[] temp;
delete[] first_part;
【问题讨论】:
I know there are several posts on this error but they are of specific case
您的情况也不例外。您对指针管理不善,超出了缓冲区和/或破坏了堆。为什么不使用std::string
而不是new char[]
?
我知道这很奇怪,但我的老师在教之前不允许我使用 std::string。他说通过使用老式的 C 数组,我会想到使用指针。
另一件事是您的操作系统(可能是 Windows)已经构建了一个 API,可以提取部分文件名(shlwapi.lib 的一部分)。无需编写自己的代码即可。
你的“老师”需要老师。了解指针并不意味着您使用new[]
和delete[]
。此外,打电话给new[]
和delete[]
也很简单,很重要——这并不能真正教给你任何东西。
你的代码有很多问题。很可能,您的老师只知道“C”,并且只是依靠他们真正知道的。
【参考方案1】:
首先,您的代码存在很多 问题,这都是由于使用 C 风格的编码而不是使用 C++。相反,如果您使用 std::string
和 C++ 流,很多这些问题都会自行解决。
第一个问题是您从未检查过文件是否存在:
FILE *fp_read = fopen(file_name, "rb");
如果 fp_read
为 NULL,则您从未检查过它,并且您的代码继续运行,就好像没有任何问题一样。这是不正确的。
然后在您的代码中执行以下操作:
FILE *fp_write = fopen(name, "wb");
同样,您继续而不检查 fp_write
是否正常,当它可能为 NULL 时。
但是让我们假设fp_read
和fp_write
不为NULL
//extract file name
char *first_part = new char[255];
char *temp = new char[255];
strcpy(temp, file_name);
上面有两个潜在的问题。
第一个问题是您没有检查以确保 file_name
少于 255 个字符。如果file_name
大于预期,则在调用strcpy
时会发生内存覆盖。使用strncpy
或memcpy
声明要复制的数字字符。
第二个问题比较微妙,就是你两次调用new[]
。如果第二次调用new[]
抛出异常怎么办?你将如何解除对new[]
的第一次调用?你不能。此外,由于抛出异常,您的输入文件仍然会被打开。
这就是为什么在这些情况下应该使用std::string
和ifstream
ofstream
。如果函数因任何原因返回,这些类型会自动释放分配的任何资源。使用 C
样式字符串和 I/O 会使您容易受到泄漏。
同样的问题:
//main process
char* name = new char[255];
strcpy(name, output_folder);
int bytesRemaining = file_size;
//create buffer
char *buffer = new char[size_of_part];
所有这些对new[]
的调用都可能是throw
,因此离开了这个函数,并留下内存泄漏和打开文件句柄。
另一个问题是您需要确保您的buffer
不会被溢出。这段代码:
long partSize;
if (bytesRemaining > size_of_part)
partSize = size_of_part;
else
partSize = bytesRemaining;
可以缩短为:
partSize = std::min(bytesRemaining, size_of_part);
这表明你的意图是什么。
另一个问题是您的bytesRemaining
在您编写输出的循环期间永远不会更新。你应该有
bytesRemaining -= partSize;
在循环中。
【讨论】:
【参考方案2】:Paul McKenzie 涵盖了大部分内容,但您的字符串处理还有一些其他问题。最好的解决方案是像他说的那样使用C++特性(std::string
、std::ifstream
、std::stringstream
、std::unique_ptr
、std::shared_ptr
等),但是如果你想做C方式,还有一些改进你可以做。
//extract file name
char *first_part = new char[255];
char *temp = new char[255];
strcpy(temp, file_name);
first_part = GetFileName(temp);
cout << endl << "File name is: " << first_part;
首先,为first_part
分配一个新的 255 字节字符串,然后在 3 行之后将其丢弃并用GetFileName(temp)
的结果替换它。 temp
本身是 file_name
的副本,但不清楚为什么,因为您从不更改它们中的任何一个。也许您收到了将file_name
传递给GetFileName()
的警告?尝试更改函数签名以添加const
:
const char *GetFileName(const char *path)
然后把上面的代码块改成:
//extract file name
first_part = GetFileName(temp);
cout << endl << "File name is: " << first_part;
现在您要处理的堆分配减少了 2 个。
现在如果你看一下这段代码:
char* name = new char[255];
strcpy(name, output_folder);
/* ... */
for (int count = 1; count <= number_of_part; count++)
sprintf(name, "%s.part_%03d", first_part, count);
这里有两个问题。第一个是name
有一个固定的大小并且不是很大,所以没有必要用new
在堆上分配它。只需将其声明为char name[255];
,就会为您处理内存。
第二个问题是您将output_folder
复制到name
,然后在几行之后将其覆盖。因此,只需删除 strcpy
。
第三个问题是first_part
可能有 255 个字节长,而name
只有 255 个字节长,所以你的sprintf
可能会溢出。我会通过使 name
至少长 15 个字节来解决这个问题,并使用 snprintf
以防万一。
结果:
char name[300];
/* ... */
for (int count = 1; count <= number_of_part; count++)
snprintf(name, sizeof(name), "%s.part_%03d", first_part, count);
【讨论】:
请避免使用或推荐std::auto_ptr
。不仅因为它已被弃用并且很快就会消失,还因为它已损坏(请参阅open-std.org/jtc1/sc22/wg21/docs/papers/2005/…)。但不要担心:有更好的工具供您使用:std::unique_ptr
和 std::shared_ptr
。
@NikBougalis 好的,删除了。我实际上并没有太多使用 C++,所以我倾向于在这些事情上落后:\以上是关于_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) C++ 文件的主要内容,如果未能解决你的问题,请参考以下文章
调试断言失败_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
_CrtIsValidHeapPointer(pUserdata) AND _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) [重复]