删除 wchar_t* 导致堆损坏

Posted

技术标签:

【中文标题】删除 wchar_t* 导致堆损坏【英文标题】:Deleting wchar_t* cause heap corruption 【发布时间】:2014-05-19 09:41:32 【问题描述】:

我有这部分代码:

class CNuovoVocabolo 
    CString m_strItaliano;
    CString m_strRusso;
    //...


void CNuovoVocabolo::GetVocabolo( CVocabolo* pVocabolo )

    int nLIta;
    int nLRus;
    wchar_t *pIta;
    wchar_t *pRus;

    nLIta = m_strItaliano.GetLength();
    nLRus = m_strRusso.GetLength();
    if( nLIta > 0 && nLRus > 0 )
    
        pIta = new wchar_t[nLIta + 1];
        wcscpy_s( pIta, nLIta * sizeof( wchar_t ), m_strItaliano );
        pRus = new wchar_t[nLRus + 1];
        wcscpy_s( pRus, nLRus * sizeof( wchar_t ), m_strRusso );
        pVocabolo->SetVocabolo( pIta, nLIta, pRus, nLRus );
        delete [] pIta; // *1
        delete [] pRus; // *2
    


// Inside CVocabolo::SetVocabolo:
void CVocabolo::SetVocabolo( const wchar_t* pIta/*=NULL*/, unsigned short nLIta/*=0*/, const wchar_t* pRus/*=NULL*/, unsigned short nLRus/*=0*/ )

    if( pIta != NULL && pRus != NULL && nLIta > 0 && nLRus > 0 )
    
        AzzeraVocabolo();
        m_nLIta = nLIta;
        m_nLRus = nLRus;
        m_pItaliano = new wchar_t[nLIta + 1];
        wcscpy_s( m_pItaliano, nLIta * sizeof( wchar_t ), pIta );
        m_pRusso = new wchar_t[nLRus + 1];
        wcscpy_s( m_pRusso, nLRus * sizeof( wchar_t ), pRus );
    

当我删除 *1 和 *2 处的 pIta 指针和 pRus 指针时,由于堆损坏而崩溃。老实说,我不明白为什么会发生这种情况以及我做错了什么。有什么建议吗?

编辑: 上网查了一下,找到了解决办法:

pIta = new wchar_t[( nLIta + 1 ) * sizeof( wchar_t )];

但老实说,我还不清楚。如果你能给我一个解释... 另外,知道这个问题为什么被否决也很好。

【问题讨论】:

wcscpy_s 看起来很可疑,尤其是乘以 sizeof (wchar_t)。 一个好的开始是停止在堆上使用指针和动态分配,并开始使用std::wstring wcs_cpy_s 的秒参数是 numberOfElements,而不是字节。所以不需要乘法。 @ivangrynko 不使用 * sizeof( wchar_t ) 会导致 Buffer too small 异常。 【参考方案1】:

问题就在这里。

pIta = new wchar_t[nLIta + 1];
wcscpy_s( pIta, nLIta * sizeof( wchar_t ), m_strItaliano );

在第一行中,大小是一个宽字符数组加上一个空终止符。这是正确的。

在第二行中,所需的大小也是字符数加上 null,但是您通过乘法给了它一个更大的数字。这是错误的。

如果这些是 short 字符和 20 个字符的字符串,则第一个参数是 21,第二个参数是 40(也应该是 21)。这本身就是一个无声的错误。不幸的是,在 DEBUG 构建(我假设您正在使用)中,wcscpy_s 函数填充了整个缓冲区,巧妙地覆盖了未分配的内存。

您的修复工作有效的原因是您(错误地)使缓冲区足够大以容纳(错误的)长度副本,并且两个 wongs 变成了白色(哎呀!)。

修复1:去掉sizeof的乘法,null加1。

修复 2:停止使用这些可怕的函数,开始使用 wstring 等编写一些好的 C++。

编辑:允许目的地为空。 (哎呀)

【讨论】:

这些函数的“安全”变体如何导致缓冲区溢出也很有趣。 好吧,我尝试删除 wcscpy_s 中的 * sizeof( wchar_t ) 但这会导致“缓冲区太小”错误。那我应该如何解决这个错误呢? 对不起,您需要允许为空(见编辑)。但是您可以通过查看 MSDN 文档轻松找到与我相同的方法。 @rubenvb:如果您查看 MS 文档,这些是不是当前的安全变体。 MS 提供了可避免这些问题的模板化安全变体。 @david.pfx 如果我没记错的话,C11 Annex K 安全 CRT 功能是在 MS 实现之后建模的(这是标准化时唯一的一个)。不过我可能错了……

以上是关于删除 wchar_t* 导致堆损坏的主要内容,如果未能解决你的问题,请参考以下文章

LPCSTR 和 wchar_t 问题。由于参数不匹配导致的链接器错误

wchar_t到QString的转换方法?

CString 如何转为wchar_t

关于wchar_t类型的输出

c_cpp 安全地将const wchar_t *复制到wchar_t *

wchar_t类型的几个函数