分享
 
 
 

CString对象的一种错误的使用方式

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

我现在做的系统有的时候会出现这样的断言失败:

Debug Error!

DAMAGE: after Normal block (#3289) at 0x182C30F0.

跟踪一下,发现问题竟出在CString的析构函数中,于是拿出了大半天的时间来研究这个问题,终于发现了原因所在。

问题的起因是我像下面这样调用无参的构造函数声明一个CString对象:

CString strText;

然后把它以这样的方式传递给别的函数:(函数1)

pVCG->GetRotDirection(WAVE_P, m_nWaveSide, strText.GetBuffer(0));

而在这个函数里对于字符串指针进行了类似于如下的操作:

sprintf(strDir, "%s", "CW");

这样做的危险性在于当字符串没有被初始化的时候,CString内部指向缓冲区的指针指向的是一个随机的地址,在CString的无参构造函数调用

了如下函数:

_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的来源如下:

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));

从上面的代码可以看出,没有进行初始化操的CString对象它们的缓冲区指针都是指向一块相同的内存:和一个全局数组相关的地址。

而在函数1例调用sprintf修改CString对象的缓冲区的结果是修改所有未初始化CString内部缓冲区指针所指,这么做是非常危险的。但是这还不是出现断言错误的原因。

接下来的错误,更难被发现。接着我的程序又调用了两次类似于下面的函数(函数2)

pVCG->GetCompressionGrade(WAVE_QRS, m_nWaveSide, 0, 60, 0, 0, strText);

在这个函数的内部有str.Format(IDS_COMPRESSION_LESS);这样的操作。

这是MFC里CString::Format的相关代码:

void AFX_CDECL CString::Format(UINT nFormatID, ...)

{

CString strFormat;//没有直接修改自己,而是先对新声明的字符串进行操作

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());//将修改后的字符串拷贝到自己的缓冲区内

ReleaseBuffer();

关键在GetBuffer:

LPTSTR CString::GetBuffer(int nMinBufLength)

{

ASSERT(nMinBufLength >= 0);

if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)

//如果指定的内存空间比已经分配的空间小的话,则重新分配,并释放掉原来的内存

{

#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;

}

由于字符串没有被初始化,所以GetData()->nAllocLength=0,因此if语句块被执行,重新在堆上分配内存,销毁原来的内存,这才第一次给字

符串分配内存。

这时还不会出现问题,接下来还会执行类似函数1的操作。

最后问题之所以发生在CString被析构的时候,原因就在于,在执行函数2的时候,字符串有了能容纳4个字节的缓冲区.如果调试的时候打开Memory窗口,在Address:文本框里输入一个堆内存的地址,可以发现VC在调试版的程序里为每个在堆里分配的内存块的后面加了4个字节的内容,值全为FD,用于检查内存越界。CString析构的时候,调用了调试版的operator delete,它就以此为依据进行了内存检测:

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));

由于后来再次调用的函数1时它产生的长度有的时候会大于4 ,就破坏了后面的边界,所以会出现这样的问题。

出现这种问题时,在调试状态下会在输出窗口输出如下类似信息:

memory check error at 0x182C7F22 = 0x57, should be 0xFD

结论:

1.所以str.GetBuffer(0)作为参数传递的时候适合于作为只读的参数;

2.如果非得要做可以修改的参数,那就得给GetBuffer传递一个保证足够安全的参数,也就是足够大;

2.如果调试版的程序出现类似

Debug Error!

DAMAGE: after Normal block (#3289) at 0x182C30F0.

的错误,应想到内存冲突。

问题终于水落石出了。反思一下,这个问题一点也不难,都怪自己基础没有打好,考虑问题不周全。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有