删除 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 问题。由于参数不匹配导致的链接器错误