CString对象的一种错误的使用方式
生活随笔
收集整理的這篇文章主要介紹了
CString对象的一种错误的使用方式
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
我現(xiàn)在做的系統(tǒng)有的時(shí)候會(huì)出現(xiàn)這樣的斷言失敗:
Debug Error!
DAMAGE: after Normal block (#328Array) at 0x182C30F0.
跟蹤一下,發(fā)現(xiàn)問(wèn)題竟出在CString的析構(gòu)函數(shù)中,于是拿出了大半天的時(shí)間來(lái)研究這個(gè)問(wèn)題,終于發(fā)現(xiàn)了原因所在。
問(wèn)題的起因是我像下面這樣調(diào)用無(wú)參的構(gòu)造函數(shù)聲明一個(gè)CString對(duì)象:
CString strText;
然后把它以這樣的方式傳遞給別的函數(shù):(函數(shù)1)
pVCG->GetRotDirection(WAVE_P, m_nWaveSide, strText.GetBuffer(0));
而在這個(gè)函數(shù)里對(duì)于字符串指針進(jìn)行了類似于如下的操作:
sprintf(strDir, "%s", "CW");
這樣做的危險(xiǎn)性在于當(dāng)字符串沒(méi)有被初始化的時(shí)候,CString內(nèi)部指向緩沖區(qū)的指針指向的是一個(gè)隨機(jī)的地址,在CString的無(wú)參構(gòu)造函數(shù)調(diào)用
了如下函數(shù):
_AFX_INLINE void CString::Init()
{ m_pchData = afxEmptyString.m_pchData; }
m_pdhData的定義:LPTSTR m_pchData;
afxEmptyString的定義是:
#define afxEmptyString AfxGetEmptyString()
const CString& AFXAPI AfxGetEmptyString()
{ return *(CString*)&_afxPchNil; }
_afxPchNil的來(lái)源如下:
AFX_STATIC_DATA int _afxInitData[] = { -1, 0, 0, 0 };
AFX_STATIC_DATA CStringData* _afxDataNil = (CStringData*)&_afxInitData;
AFX_COMDAT LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData)+sizeof(CStringData));
從上面的代碼可以看出,沒(méi)有進(jìn)行初始化操的CString對(duì)象它們的緩沖區(qū)指針都是指向一塊相同的內(nèi)存:和一個(gè)全局?jǐn)?shù)組相關(guān)的地址。
而在函數(shù)1例調(diào)用sprintf修改CString對(duì)象的緩沖區(qū)的結(jié)果是修改所有未初始化CString內(nèi)部緩沖區(qū)指針?biāo)?#xff0c;這么做是非常危險(xiǎn)的。但是這還不是出現(xiàn)斷言錯(cuò)誤的原因。
接下來(lái)的錯(cuò)誤,更難被發(fā)現(xiàn)。接著我的程序又調(diào)用了兩次類似于下面的函數(shù)(函數(shù)2)
pVCG->GetCompressionGrade(WAVE_QRS, m_nWaveSide, 0, 60, 0, 0, strText);
在這個(gè)函數(shù)的內(nèi)部有str.Format(IDS_COMPRESSION_LESS);這樣的操作。
這是MFC里CString::Format的相關(guān)代碼:
void AFX_CDECL CString::Format(UINT nFormatID, ...)
{
CString strFormat;//沒(méi)有直接修改自己,而是先對(duì)新聲明的字符串進(jìn)行操作
VERIFY(strFormat.LoadString(nFormatID) != 0);
?
va_list argList;
va_start(argList, nFormatID);
FormatV(strFormat, argList);
va_end(argList);
}
而在void CString::FormatV(LPCTSTR lpszFormat, va_list argList)里最后作如下操作:
GetBuffer(nMaxLen);
VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());//將修改后的字符串拷貝到自己的緩沖區(qū)內(nèi)
ReleaseBuffer();
關(guān)鍵在GetBuffer:
LPTSTR CString::GetBuffer(int nMinBufLength)
{
ASSERT(nMinBufLength >= 0);
?
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
//如果指定的內(nèi)存空間比已經(jīng)分配的空間小的話,則重新分配,并釋放掉原來(lái)的內(nèi)存
{
#ifdef _DEBUG
??????? // give a warning in case locked string becomes unlocked
??????? if (GetData() != _afxDataNil && GetData()->nRefs < 0)
??????? ?????? TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!\n");
#endif
// we have to grow the buffer
?????? CStringData* pOldData = GetData();
??????? int nOldLen = GetData()->nDataLength;?? // AllocBuffer will tromp it
??????? if (nMinBufLength < nOldLen)
??????? ?????? nMinBufLength = nOldLen;
?????? AllocBuffer(nMinBufLength);
?????? memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
?????? GetData()->nDataLength = nOldLen;
?????? CString::Release(pOldData);
}
ASSERT(GetData()->nRefs <= 1);
?
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL);
return m_pchData;
}
由于字符串沒(méi)有被初始化,所以GetData()->nAllocLength=0,因此if語(yǔ)句塊被執(zhí)行,重新在堆上分配內(nèi)存,銷毀原來(lái)的內(nèi)存,這才第一次給字
符串分配內(nèi)存。
這時(shí)還不會(huì)出現(xiàn)問(wèn)題,接下來(lái)還會(huì)執(zhí)行類似函數(shù)1的操作。
最后問(wèn)題之所以發(fā)生在CString被析構(gòu)的時(shí)候,原因就在于,在執(zhí)行函數(shù)2的時(shí)候,字符串有了能容納4個(gè)字節(jié)的緩沖區(qū).如果調(diào)試的時(shí)候打開(kāi)Memory窗口,在Address:文本框里輸入一個(gè)堆內(nèi)存的地址,可以發(fā)現(xiàn)VC在調(diào)試版的程序里為每個(gè)在堆里分配的內(nèi)存塊的后面加了4個(gè)字節(jié)的內(nèi)容,值全為FD,用于檢查內(nèi)存越界。CString析構(gòu)的時(shí)候,調(diào)用了調(diào)試版的operator delete,它就以此為依據(jù)進(jìn)行了內(nèi)存檢測(cè):
if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize))
_RPT3(_CRT_ERROR, "DAMAGE: after %hs block (#%d) at 0x%08X.\n",
??????????????????? szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
??????????????????? pHead->lRequest,
??????????????????? (BYTE *) pbData(pHead));
由于后來(lái)再次調(diào)用的函數(shù)1時(shí)它產(chǎn)生的長(zhǎng)度有的時(shí)候會(huì)大于4 ,就破壞了后面的邊界,所以會(huì)出現(xiàn)這樣的問(wèn)題。
出現(xiàn)這種問(wèn)題時(shí),在調(diào)試狀態(tài)下會(huì)在輸出窗口輸出如下類似信息:
memory check error at 0x182C7F22 = 0x57, should be 0xFD
結(jié)論:
1.所以str.GetBuffer(0)作為參數(shù)傳遞的時(shí)候適合于作為只讀的參數(shù);
2.如果非得要做可以修改的參數(shù),那就得給GetBuffer傳遞一個(gè)保證足夠安全的參數(shù),也就是足夠大;
2.如果調(diào)試版的程序出現(xiàn)類似
Debug Error!
DAMAGE: after Normal block (#328Array) at 0x182C30F0.
的錯(cuò)誤,應(yīng)想到內(nèi)存沖突。
問(wèn)題終于水落石出了。反思一下,這個(gè)問(wèn)題一點(diǎn)也不難,都怪自己基礎(chǔ)沒(méi)有打好,考慮問(wèn)題不周全。
總結(jié)
以上是生活随笔為你收集整理的CString对象的一种错误的使用方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: KMP 算法并非字符串查找的优化 [转]
- 下一篇: 如何防止android软件被反编译,破解