C++堆内存错误:CRT detected that the application wrote to memory before start of heap buffer
Posted dvlinker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++堆内存错误:CRT detected that the application wrote to memory before start of heap buffer相关的知识,希望对你有一定的参考价值。
最近测试发现一个掩藏很深的bug,这个只有在特定的操作场景下才会出现,和测试数据及场景有强相关性。好在我们找到了复现的办法,直接在Debug调试下复现了问题。复现问题后,弹出了如下的报错提示框:
根据提示信息,检测到了堆内存被破坏,程序向堆内存前面的内存区域写入了内容。系统在分配堆内存时,会在给用户使用的堆内存前后加上头信息和尾信息,用来维护和管理这些堆内存,这些堆内存的头部内存和尾部内存区域,应用程序是不能写入的,是系统来维护和管理的。
本例是堆内存越界到内存的头部内存区域,而一般情况是越界到内存尾部的,越界到头部则是比较少见的。当然这不排除内存越界的比较长,直接越到另一段堆内存的头部内存区域的。
VS检测到异常后中断下来,发现是崩溃在对堆内存的delete释放代码上:
按讲释放一段申请的堆内存是不应该有问题的,如果在new或者delete时报错,排除对堆内存delete两次的场景,那基本可以断定是堆内存被破坏了,导致new或delete出现异常。
至于到底是什么情况,需要去结合代码去分析了。于是去查看出问题函数的代码上下文,查看相关变量的值,发现我们在new出一段堆内存后,使用数组下标去访问该段堆内存,结果在当前的场景下,数组下标出现了-1的情况,对该-1下标对应的内存区域进行了赋值,所以就越界到了堆内存的头部区域,然后被系统检测到了,系统产生了异常。相关代码如下所示:
void CRenderEngine::DrawText( HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle )
ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);
if (pstrText == NULL || pManager == NULL) return;
::SetBkMode(hDC, TRANSPARENT);
::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));
HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont));
// 检查一下待绘制的字符串中是否有换行符,如果有换行符,则不能使用下面处理省略号的算法
// 解决SDM-00125710
bool bHasCarriageReturn = false;
CStdString strTmp = pstrText;
if ( strTmp.Find( _T("\\n") ) != -1 )
bHasCarriageReturn = true;
else if ( strTmp.Find( _T("\\r\\n") ) != -1 )
bHasCarriageReturn = true;
if ( (uStyle & DT_WORDBREAK) || bHasCarriageReturn )
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
else if( uStyle & (DT_END_ELLIPSIS | DT_MODIFYSTRING) )
// 为了解决endellipsis的省略号显示不全问题:
// 1)有些情况下省略号的三个点显示不全;
// 2)win7下省略号显示正常,win10下不显示省略号
// 现做如下处理,参考
// https://www.codeproject.com/articles/7079/implementing-the-dt-end-ellipsis-flag-on-the-pocke
// 注意:下面的代码只能处理单行文字显示时的结尾省略号的问题
int nCount = 0;
int nWidth = rc.right - rc.left;
SIZE szStr = 0, 0 ;
LPTSTR lpStr = NULL;
// 计算需绘制的字符串字符个数
nCount = _tcslen(pstrText);
if( nCount == 0 || nCount == -1 )
// 0是没有字符需要绘制,直接返回,-1是计算字符串长度出错
::SelectObject(hDC, hOldFont);
return;
// 复制一份文字内容
lpStr = new TCHAR[nCount+1];
ZeroMemory( lpStr , (nCount+1)*sizeof(TCHAR) );
_tcscpy_s( lpStr, nCount+1, pstrText );
lpStr[nCount] = _T('\\0');
// 检查如果字符串长度大于绘制区域宽度
GetTextExtentPoint32( hDC, lpStr, nCount, &szStr );
if( szStr.cx > nWidth && nWidth > 0 )
// 估算出绘制区域能够显示的字符数量
// 估算公式:nWidth/nEstimate = szStr.cx/nCount
// 该公式假定字符串中的字符宽度是相等的,实际上数字或字母与中文的宽度是不同的,此处
// 只是粗略的估计
int nEstimate = (nCount * nWidth) / szStr.cx + 1;
if(nEstimate < nCount)
nCount = nEstimate;
const DWORD dwEllipsisUnicode = 0x2026;
// 0x2026对应省略号三个点的Unicode编码
lpStr[nCount-1] = (TCHAR)dwEllipsisUnicode;
GetTextExtentPoint32( hDC, lpStr, nCount, &szStr );
// 当延伸的内容大于绘制区域的宽度,移除这个字符
while(szStr.cx > nWidth && nCount > 1)
lpStr[--nCount] = 0; // 移除字符
lpStr[nCount-1] = (TCHAR)dwEllipsisUnicode; // 用...代替
GetTextExtentPoint32(hDC, lpStr, nCount, &szStr);
::DrawText(hDC, lpStr, nCount, &rc, uStyle | DT_NOPREFIX );
delete[] lpStr;
lpStr = NULL;
else
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
else
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
::SelectObject(hDC, hOldFont);
我们一定要注意,在使用表达式作为数组下标去访问内存时,一定要注意对数组下标值做保护,放置出现异常的值,比如负值或者超过数组长度的值,我们在此处添加一个保护即可。
以上是关于C++堆内存错误:CRT detected that the application wrote to memory before start of heap buffer的主要内容,如果未能解决你的问题,请参考以下文章
C语言错误: CRT detected that the application wrote to memory after end of heap buffer
我该如何解决这个错误? CRT 检测到应用程序在堆缓冲区结束后写入内存
[vs执行报错] CRT detected that the application wrote to memory after end of heap buffer
C++的newdelete需要注意的一点:使用危险函数导致的越界CRT detected that the application wrote to memory after end of heap