动态分配LPTSTR (TCHAR*)和delete[]操作符的不稳定行为

Unstable behavior of dynamically allocated LPTSTR (TCHAR*) and delete[] operator?

本文关键字:操作符 不稳定 delete LPTSTR TCHAR 动态分配      更新时间:2023-10-16

我试图构建一个处理String操作符的类。然而,由于没有令人信服的原因,它有时会在delete[]操作期间崩溃。我使用strsafe库来完成所有的内部字符串操作。

//qstring

LPTSTR m_string;
void QString::operator +=(const QString &_in) //Concat m_string with _in.m_string
{
    size_t size = strlength(m_string) + strlength(_in.m_string) +1; //new size
    LPTSTR buffer = new TCHAR[size]; //alloc buffer
    ::StringCchCopy(buffer,strlength(m_string)+1,m_string); //copy current m_string to buffer
    ::StringCchCat(buffer, size, _in.m_string); //concat buffer with the input
    Replace(buffer); //replace this object with m_string
    delete[] buffer; //dealloc
}

void QString::Replace(LPCTSTR src) //replace m_string with src
{
    size_t size = strlength(src)+1; //new size
    Alloc(size);
    ::StringCchCopy(m_string,size,src); //copy src to m_string
}

void QString::Alloc(size_t size) //Dynamic allocation
{
    if(m_string != NULL) Free();
    m_string = new TCHAR[size+1];
}

void QString::Free()  //Free m_string
{
        delete[] m_string; //Sometime crashes here
        m_string = NULL;
}

QString ToStr(int _in) //Convert Integer to qstring
{
    int size = 1;
    int f = _in;
    while(f > 0)
    {
        f /=10;
        size++;
    }
    TCHAR* buf = new TCHAR[size];
    for(int i = 0; i < size; i++) buf[i] = (TCHAR)TEXT("");
    QString result(L"undef");
    if(::_itow_s(_in,buf,size,10) == 0) //No error code = ok 
    {
        result = buf;
    }
    delete[] buf;
    return result;
}

//示例1:不崩溃

int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TEST";
    a += ToStr(1000);
::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);
        return 0;
}

//示例2:打印奇怪的字符加上一些当前字符(Unicode问题?)

 int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TESTTESTESTEST";
    a += ToStr(1000);
    ::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);
    return 0;
}

//示例3:加载时崩溃

int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TESTTESTESTEST";
    a += ToStr(1000);
    a += L"TESTESTEST";
    a += ToStr(100);
    ::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);
    return 0;
}

在Free()操作delete[]操作符时崩溃。错误为

HEAP[StringTest.exe]: Invalid Address specified to RtlFreeHeap(003C0000, 003C46A8)

HEAP[StringTest.exe]:堆块在003C4750修改在003C475C超过请求的大小4

你的构造函数没有初始化m_string为NULL(根据你自己上面的评论)。这将导致随机的Free()失败。

正如selbie所指出的,这段代码中还有其他bug和低效率。例如,Replace总是重新分配,即使从已经分配的+=调用也是如此。并且您在Replace和Alloc中都添加了1,这表明您不清楚哪些函数的值包含终止符,哪些不包含终止符。

除非这是一个学习或家庭作业练习,否则我强烈建议不要编写自己的字符串类-与使用std::string(或者ATL::CString,如果您喜欢)相比,它将花费您更多的工作来获得合理和高效的内容。

Martyn

另一个答案:

我的灵力告诉我,如果QString没有复制构造函数,那么当ToStr()返回堆栈上QString的"副本"时,可能会发生不好的事情。

我看到很多bug。

对于初学者来说,如果你将0传递给ToStr函数,你将只分配一个字节的内存来保存字符串"0",这是两个字节('0' + null char)。此外,如果向这个函数传递一个负整数,还会发生其他不好的事情。你最好分配比你需要的更多,而不要试图计算"确切"的大小。

int size = sizeof(_in) * 4 + 1;

没有一个你的字符串(即使使用stringch函数)让我感觉很好。

我建议分配2倍的"大小"你需要,然后仔细研究缓冲区和m_string的内存地址通过调试,如果strcpy超过"size-1"字节(不包括null终止字符)。

您的整个分配和自由算法是基于m_string已初始化为NULL的事实。你在构造函数中这样做了吗(我在这里没有看到)?

顺便说一下,

可能与名为Alloc()的CRT库函数有冲突。http://msdn.microsoft.com/en-us/library/dd492420%28VS.100%29.aspx

计算整型字符串长度的算法不正确。从size=1开始,应该从size=0开始。此外,您还需要处理特殊情况,其中0==f: 0绝对是一个数字,并且占用1个字符。你有一个偏离1的错误(栅栏错误),整数的大小总是太大1。

QString ToStr(int _in) //Convert Integer to qstring
{
    int size = 0;
    int f = _in;
    if (0==f) {
        size=1;
    } else {
        while(f > 0) {
            f /=10;
            size++;
        }
    }

在Replace()中,你Alloc() 2个额外的字符。

size_t size = strlength(src)+1;

应为

size_t size = strlength(src);

,如果你需要一个额外的字符::StringCchCopy(m_string,size,src);在这里输入size+1。或者更改您的Alloc,以便它分配您所要求的字符数量(如名称所示)。