您的位置:首页 > 编程语言 > C语言/C++

VC++学习笔记(3)------- CString相关的一些问题以及LineLength()的

2009-12-24 09:16 363 查看
先说说CString。

CString 是一种很有用的数据类型,确切的说它是个类。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。然而如果使用时不注意就会出现问题,而且很难查找问题原因。

我们完全可以将它作为字符串类型,像其他数据类型一样来使用它,它可以与char * 进行相互转化。最简单的一种是直接进行强制转化,而且并不需要强制类型转换操作符。但要注意的是CString只能强制转化为LPCTSTR,也就是常量字符串指针。这就意味着我们不能改变指针指向的内容。很多函数的参数类型都是LPTSTR,也就是字符串指针(注意与之前的向区别),如果我们想把CString转化为LPTSTR传递进去,就不能强制转化了,应该使用CString的GetBuffer方法,然后还要调用ReleaseBuffer 释放,并且在 GetBuffer 和 ReleaseBuffer 之间这个范围,一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。

(更详细地参考http://baike.baidu.com/view/998109.htm#4

今天写了个程序,先用无参构造函数声明一个CString对象,然后在for循环中使用GetBuffer()获取CString->LPTSTR的指针传递给其他函数。运行时总是出现“程序遇到问题需要关闭”这样的错误提示,无论怎么改都是这样。一鼓作气跟踪调试,发现问题竟出现在CString对象的析构函数中。还好网上有高人也遇到了这样的问题,仔细研究一下吧。

高人指出,调用无参的构造函数声明一个CString对象,然后通过GetBuffer()传递给其他函数进行类似内容的操作,这样做的危险性在于当字符串没有被初始化的时候,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对象它们的缓冲区指针都是指向一块相同的内存:和一个全局数组相关的地址。修改CString对象的缓冲区的结果是修改所有未初始化CString内部缓冲区指针所指,这么做是非常危险的。这并不是出现错误提示的原因。最后问题之所以发生在CString被析构的时候,原因就在于,在函数第一次执行的时候,字符串有了能容纳几个字节的缓冲区。如果调试的时候打开Memory窗口,在Address:文本框里输入一个堆内存的地址,可以发现VC在调试版的程序里为每个在堆里分配的内存块的后面加了4个字节的内容,值全为FD,用于检查内存越界。CString析构的时候,调用了调试版的operator delete,它就以此为依据进行了内存检测。当函数再次执行时,有可能出现需要更大缓冲区的情况,这时就会破坏后面的边界,出现内存冲突,也就是之前出现的错误提示。因此得出结论如下:
1.所以str.GetBuffer(0)作为参数传递的时候适合于作为只读的参数;
2.如果非得要做可以修改的参数,那就得给GetBuffer传递一个保证足够安全的参数,也就是足够大;
2.如果调试版的程序出现类似:Debug Error!
DAMAGE: after Normal block (#3289) at 0x182C30F0. 的错误,应想到内存冲突。
(详细的参考:http://www.chinaitpower.com/A200507/2005-07-27/169218.html
根据提示我将所需的缓冲区扩大了100,之后就没有再出现问题了。
接下来是CRichEditCtrl类的LineLength方法的正确使用。
顾名思义,LineLength方法的作用是获取CRichEditCtrl的某一行的长度。该函数的原型如下 int LineLength(int nLine = -1) const; 这里的nLine是Index,也就是索引。我今天使用时理所当然地认为该索引就是行索引,就这样使用:
for (i=0;i < nLineCount;i++)

{

nLineLength = pmyRichEditCtrl->LineLength(i);
……

}
结果得到的总是第一行的长度,超级郁闷,百思不得其解。
还得诉诸网络啊~
事实上这里参数nLine的含义跟我们平常接触到的大多数概念不完全一样,它并不是行数,而是该rich edit control中的字符位置,根据此位置计算位于第几行,该函数就获取的是第几行的长度,如果给出的值为-1,则获取的是当前行(光标所在行)的长度。正确的写法应该是:
for (i=0;i < nLineCount;i++)

{

nLineLength = pmyRichEditCtrl->LineLength(pmyRichEditCtrl->LineIndex(i));
……

}
这样就搞定了。
今天下午以及晚上为了查找这些错误真是累死我了,不过最后都搞定了,还是有收获的啊。把这些记录下来,方便以后查找,也是对自己辛苦的慰藉,感觉不错~明天接着努力!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: