_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_readfp_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 时会发生内存覆盖。使用strncpymemcpy 声明要复制的数字字符。

第二个问题比较微妙,就是你两次调用new[]。如果第二次调用new[] 抛出异常怎么办?你将如何解除对new[] 的第一次调用?你不能。此外,由于抛出异常,您的输入文件仍然会被打开。

这就是为什么在这些情况下应该使用std::stringifstream 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::stringstd::ifstreamstd::stringstreamstd::unique_ptrstd::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_ptrstd::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) [重复]

PHP 合并2个链表

替代 C/C++ localtime()

beanstalkd 消息队列发邮件

合并两个排序的链表