【问题标题】:Error when trying to deallocate pointer of char array: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)尝试释放 char 数组的指针时出错:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
【发布时间】:2014-04-10 15:30:42
【问题描述】:

我正在编写一个使用 Boost.Asio 发送和接收图像的 C++ 程序。

编译时我没有收到错误,但在执行并发送图像时,接收图像的程序崩溃并给出以下错误消息(在 Visual Studio 2012,Windows 7 32 位中):

调试断言失败:

程序:[…]\DataSender.exe
档案:f:\dd\vctools\crt_bld\self_x86\crt\src\dbgdel.cpp
线路:52

表达式:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

我将 4096 字节大小的包读取到一个指向 char 数组的指针中,同时仍有要读取的传入字节。在最后的循环中——如果要读取的字节数少于 4096——我删除指针并创建一个指向剩余字节大小的 char 数组的指针。直到这里它仍然有效。

但是当我尝试在循环结束时再次删除 char 指针数组(以便为下一个传入的图像创建一个标准大小为 4096 的新 char 指针数组)时,程序崩溃了。

这是我的代码摘录:

char* buffer = new char[4096];

[…]

int remainingBytes = imageSize;

[…]

// read data
while( remainingBytes > 0 )
{
    boost::system::error_code error;

    // use smaller buffer if remaining bytes don't fill the tcp package
    // fully
    if( remainingBytes < 4096 )
    {
        delete[] buffer; // this one doesn't give an error
        bufferSize = remainingBytes;
        char* buffer = new char[bufferSize];
    }

    // read from socket into buffer
    size_t receivedBytes = socket.read_some(
            boost::asio::buffer(buffer, bufferSize), error);
    remainingBytes -= receivedBytes;

    // count total length
    totalReceivedBytes += receivedBytes;

    // add current buffer to totalBuffer
    for( int i = 0; i < bufferSize; i++)
    {
        totalBuffer.push_back(buffer[i]);
    }

    // if smaller buffer has been used delete it and
    // create usual tcp buffer again
    if( receivedBytes < 4096 )
    {
        delete[] buffer; // here the error occurs
        bufferSize = 4096;
        char* buffer = new char[bufferSize];
    }
}

我也在 Debian GNU/Linux 7.2 64 位机器上运行了相同的代码,它在代码中的相同位置返回了以下错误:

*** glibc detected *** ./datasender: double free or corruption (!prev): 0x0000000002503970 ***

我认为在释放 char 指针数组时我做错了什么,但我还没有弄清楚。

有人能指出正确的方向吗?

【问题讨论】:

  • delete[buffer]; 这很好奇,你不想要delete [] buffer; 吗? (只是问,我对你的说法有点困惑)。哦,顺便说一句,您在 if 语句中分配的 buffer 是 if 范围的本地。你在这里漏水了。
  • 使用 std::vector。 1)它更安全,更容易使用,2)它可能会比你现在正在做的新/删除体操运行得更快。如果缓冲区恰好更小,使用向量调整缓冲区大小将不会通过调用 new[]/delete[] 的过程。
  • 确实没有理由调整缓冲区大小以使其更小。只需跟踪接收到的数据量,然后只读出那么多数据。
  • @JBL:糟糕,这只是一个错字。在实际代码中它是delete[] buffer。但是谢谢,我不知道如果我在循环内部或外部声明某些内容会有所不同。

标签: c++ arrays pointers boost-asio delete-operator


【解决方案1】:

remainingBytesreceivedBytes 小于 4096 时,您实际上删除了两倍的缓冲区。

确实,您要删除一次buffer,然后将内存分配给本地buffer,而不是外部的

然后,当您在第二个 if 块中删除 buffer 时,您将第二次删除同一个缓冲区。您在 if 范围内所做的分配是内存泄漏。这些不是相同的变量。

当你这样做时

char* buffer = new char[bufferSize];

在您的 if 范围内,您正在创建一个新变量,而不是将内存分配到外部 buffer 变量中。因此,您正在泄漏,并且没有将内存分配到刚刚删除的缓冲区中。

不用多看,你应该删除两个 if 块中buffer 前面的char*,然后继续调试。

【讨论】:

  • 非常感谢!现在它正在工作。我以为函数内部和外部的局部变量和全局变量之间只有区别——但仔细想想,我知道 for 循环的迭代器是局部的,但显然我并没有那么清楚。
  • 两件事:1) 考虑变量名可见性的范围。不仅是函数,还有 if 块、循环等。 2) 正如许多其他人指出的那样,您绝对应该考虑使用像 std::vector 这样的标准容器来完成您正在做的事情。你会省去很多导致这个问题的内存恶作剧。 :)
【解决方案2】:

我会使用 std::vector 代替:

#include <vector>
//...
std::vector<char> buffer(remainingBytes);
bufferSize = remainingBytes;
//...
while( remainingBytes > 0 )
{
    boost::system::error_code error;

    // use smaller buffer if remaining bytes don't fill the tcp package
    // fully
    if( remainingBytes < 4096 )
    {
       buffer.resize(remainingBytes);
       bufferSize = remainingBytes;
    }

    // read from socket into buffer
    size_t receivedBytes = socket.read_some(
            boost::asio::buffer(&buffer[0], bufferSize), error);
    remainingBytes -= receivedBytes;

    // count total length
    totalReceivedBytes += receivedBytes;

    // add current buffer to totalBuffer
    totalBuffer.insert(totalBuffer.end(), buffer.begin(), 
                                          buffer.begin() + receivedBytes);

    // if smaller buffer has been used delete it and
    // create usual tcp buffer again
    if( receivedBytes < 4096 )
    {
        buffer.resize(4096);
        bufferSize = 4096;
    }
}

不会有内存泄漏。

另外,我认为您的代码有一个错误,即您应该只复制接收到的字节数(read_some() 函数的返回值)。相反,您假设返回了 bufferSize 个字符。

【讨论】:

    猜你喜欢
    • 2012-08-20
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-31
    • 1970-01-01
    • 2015-02-11
    相关资源
    最近更新 更多