CString,string,char*,char[],GetBuffer(int) 秒速五厘米 2022-06-12 03:53 282阅读 0赞 > ## [http://hi.baidu.com/luosiyong/item/df7a7e8f03f311d75e0ec10d][http_hi.baidu.com_luosiyong_item_df7a7e8f03f311d75e0ec10d] ## > > ## char数组与指针的区别 ## > > ![SouthEast][] > > 测试代码如下: > > #include <windows.h> > #include <stdio.h> > > char g_a[] = "global array"; > char *g_p = "global pointer"; > > DWORD WINAPI ArrayThreadProc(LPVOID param) > { > char a[] = "thread array"; > //printf("addr ta: 0x%08X\n", a); > *(char **)param = a; > return 0; > } > > DWORD WINAPI PointerThreadProc(LPVOID param) > { > char *p = "thread pointer"; > //printf("addr tp: 0x%08X\n", p); > *(char **)param = p; > return 0; > } > > void TestArray(char **str) > { > char a[] = "temp array"; > //printf("addr a: 0x%08X\n", a); > a[0] = 'T'; > *str = a; > } > > void TestPointer(char **str) > { > char *p = "temp pointer"; > //printf("addr p: 0x%08X\n", p); > //p[0] = 'T'; //该内存不能为written > *str = p; > } > > int main() > { > //printf("addr g_a: 0x%08X\n", g_a); > //printf("addr g_p: 0x%08X\n", g_p); > > g_a[0] = 'G'; > //g_p[0] = 'G'; //该内存不能为written > > char *str; > > TestArray(&str); //局部变量释放之后, str为野指针 > //puts(str); > > TestPointer(&str); > puts(str); > > HANDLE handle; > > handle = CreateThread(0, 0, ArrayThreadProc, &str, 0, 0); > WaitForSingleObject(handle, INFINITE); > CloseHandle(handle); > //puts(str); //该内存不能为read > > handle = CreateThread(0, 0, PointerThreadProc, &str, 0, 0); > WaitForSingleObject(handle, INFINITE); > CloseHandle(handle); > puts(str); > > return 0; > } > > 编译成了exe之后,用UltraEdit打开,能找到如下字符串: > > ![91939fccf4fd8d7c01e928bb.jpg][] > > 能找到: > > global array > > global pointer > > thread pointer > > temp pointer > > 写在代码中的字符串 thread array 和 temp array 在exe 中则找不到。 > > 1、全局char \[\] 和 char \*都保存在程序区,一个是分配数组大小(看数组声明的大小或者初始化字符串的尺寸)的空间,一个是分配指针大小(4 byte)的空间。 > > 2、全局char a\[\] = "array", char \*p = "pointer", 字符串 "array" 和 "pointer" 都放在常量区。初始化的时候,"array"被拷贝到a中,p是直接指向常量区的 "pointer"。因此,a数组空间是可写的,p指向的内容是不可写的。 > > 3、临时变量的 char a\[\] 和 char \*p,a 和 p都是在栈空间分配内存,a分配数组大小的内存,p分配指针大小的内存。a用等号右边的字符串初始化数组自身,p指向等号右边的常量字符串(存储在常量区里的)。因此,a也是可写的,p指向的内容也是不可写的。 > > 4、临时变量,超出作用域的时候,数组空间被回收了,指针自身的空间(4 byte)被回收了。数组的内容已经不存在了,如果把数组地址赋值给一个指针,就成了野指针,此时,它不可读。由于指针p指向的空间是常量区,在程序退出前不会回收,因此,此时这个区域还可读。 > > 5、结合上面代码,当创建线程的时候,线程退出之后,那部分空间就完全没有权限读了,而同一个线程,野指针可读,但是读出来已经是乱码。 > > 6、一个问题:上面代码中的 "thread array" 和 "temp array" 没有存储在exe中,那exe在运行的时候,怎么知道用这两个字符串去初始化那数组的呢? > 7、以上仅个人理解,如有错误,请指正。 > > > > ================================== > > ## char c='A'; ## > > char szCr\[\]="ABC"; > > char \*p和char p\[\]??? 参阅:[http://topic.csdn.net/u/20080608/21/12a02aab-ce19-4e9c-8e75-6f09da4144f3.html][http_topic.csdn.net_u_20080608_21_12a02aab-ce19-4e9c-8e75-6f09da4144f3.html] > > ## ============================================ ## > > ## std-string、char -和char \[\]的问题解答 ## > > stringstr\_s="abc"; > char\*str\_cp="abc"; > charstr\_ca\[\]="abc"; > > 1.str\_cp是指针,指向一段数据有四个元素,离别是'a','b','c','/0'。 > > 2.str\_ca是数组,它包括四个元素,离别是'a','b','c','/0'。 > > > 引用:我懂得\*str\_cp的"abc"多了一个'/0' > > > 这个说法不准确。数组str\_ca也会有'/0'。但万一你写charstr\_ca\[\]=\{'a','b','c'\}则不会有'/0'。 > > 3. > > > 引用:str\_ca相当于一个char\*conststr\_cp > > > 这个说法不准确。数组和指针是不同的,数组不能看作常指针。凭据即便: > char\*consts="abcdef"; > chara\[\]="abcdef"; > 则sizeof(s)等闲为2可能4,而sizeof(a)为7。 > 指针只保留了所指向的地址,数组既包括首地址的消息,还包括长度的消息。不过数组在作为参数递交时,能够改换为指针的形式(数组首地址作为指针所指地址,数组长度消息被丢弃)。 > > 4. > > > 引用:stringstr\_s性质上是什么呢? > > > 从对象的见解看。str\_s即便一个对象,其中包括了字符串数据,如此而已。至于对象内部是如何垄断,以保证准确的包括这个字符串数据,这全面取决于string类的作者。这种假象揭示了面向对象编程信念中的一个重要见解——封装。作为类的利用者,我们凡是懂得它该当怎么用即可,无须要懂得它是如何工作的。也即便说无须要懂得它的性质。 > 当然我们也不能一无所知。为了更好的利用这个类,它的大约个性要了然于心。例如,我们要懂得string中的字符是继续存储的,而不是穿越链表来举行存储的。等等。 > > 5. > > > 引用:等闲做开发都得转换成char\*的吗? > > > 不是的。C++本身的输入输出就直接扶持string。例如: > strings; > cin>>s; > cout<<s; > 固然string是激励的,但这并不料味着char\*即便该当被丢弃的。许多时候还是会用到char\*。例如你从zip包中解压出一个文件,因为许多zip收缩解压的过程都用C语言写,因而文件解压获得的数据在内存中很可能即便以char\*的形式存在的。 > > 6. > > > 引用:我能够在一个函数里归来一个数组名目/string变量/char\*,然后给一个char\*/string/char\[\]这么赋值行吗? > > > 这个问题比拟细,下面一点一点的分析。 > (1)归来一个char\*,赋值给char\*。能够。 > (2)归来一个char\*,赋值给string。能够。运行时切实的情形是,依据char\*的值构造一个临时的string对象,把这个临时的string对象赋值给你的string变量,最后烧毁临时的string对象。 > (3)归来一个char\*,赋值给char\[\]。不能够。char\[\]是数组,数组是不批准赋值的。只有数组的元素能力赋值。 > (4)归来一个string变量,赋值给char\*。不能够。因为string不能改换为char\*。你能够穿越string类的c\_str措施获得它的指针,然而要当心,万一string对象本身被烧毁了,则指针所指的数据也跟着无效。例如: > stringfunc()\{ > returnstring("abc"); > \} > intmain()\{ > char\*s=func().c\_str(); > cout<<s;//讹谬!! > \} > 因为函数func()归来后,它的归来值是临时的。穿越c\_str获得它的指针赋值给s后,临时值被烧毁,因而s指向的是一个无效的位置。 > (5)归来一个string变量,赋值给string。能够。 > (6)归来一个string变量,赋值给char\[\]。不能够。char\[\]是数组,数组是不批准赋值的。只有数组的元素能力赋值。 > (7)万一归来数组的名字,其实是归来数组的首地址,也即便一个指针。因而这其实跟归来char\*是极其相仿的。单一必需当心的问题是,万一数组是局部变量,并且不是static的,则函数归来时数组会被烧毁,因而归来的指针所指向的位置也是无效的。例如: > char\*func()\{ > chara\[\]=\{"abc"\}; > returna; > \} > intmain()\{ > char\*s=func(); > cout<<s;//讹谬!! > \} > > 7. > > > 引用:我万一cout<<str\_cp\[1\]此刻指向'b'万一以'/0'为告终符号的话,该当输出的是bc啊 > > > 这个波及到数据种类的问题。cout会依据数据的种类和数据的值来确定最后的输出。对于char种类,cout只输出一个字符,对于char\*种类,cout输出多个字符,直到遭到'/0'才告终。 > 因为str\_cp是char\*种类,因而str\_cp\[1\]是char种类。因而cout<<str\_cp\[1\]切实上是输出char种类,因而只输出一个字符。 > 但str\_cp+1是char\*种类,因而cout<<(str\_cp+1)切实上是输出char\*种类,因而输出多个字符,遭到'/0'才告终。 > 万一感受(str\_cp+1)不够直观,能够写为&str\_cp\[1\]。(固然后一种写法看起来厉行了两步垄断,但切实上两种写法编译后获得的代码是一样的,不会管用率问题) > > > > > ## ============================================ ## > > 这几天研究VC,做个多线程的服务器。就在CString上屡受挫折,痛定之后学习了网上一些高手的文章(其实还是msdn写的好,只不过英文的看着费劲),有点理解了。**其实最关键的是CString是个类,而且是引用,有些操作有可以直接操作内存。**这就在CString特殊又容易搞错的地方,我在这拼出一些网上我觉得很好文章,互相学习。 > > 串操作是编程中最常用也最基本的操作之一. 做为VC程序员,无论是菜鸟或高手都曾用过CString.而且好像实际编程中很难离得开它(虽然它不是标准C++中的库).因为MFC中提供的这个类对我们操作字串实在太方便了,CString不仅提供各种丰富的操作函数、操作符重载,使我们使用起串起来更象basic中那样直观;而且它还提供了动态内存分配,使我们减少了多少字符串数组越界的隐患。但是,我们在使用过程中也体会到CString简直太容易出错了,而且有的不可捉摸。所以有许多高人站过来,建议抛弃它。 > 其实CString封装得确实很完美,它有许多优点,如“容易使用,功能强,动态分配内存,大量进行拷贝时它很能节省内存资源并且执行效率高,与标准C完全兼容,同时支持多字节与宽字节,由于有异常机制所以使用它安全方便” 其实,使用过程中之所以容易出错,那是因为我们对它了解得还不够,特别是它的实现机制。因为我们中的大多数人,在工作中并不爱那么深入地去看关于它的文档,何况它还是英文的。 > > > 因此为了减少频繁的申请内存或者释放内存,CString会先申请一个大的内存块用来存放字符串。这样,以后当字符串长度增长时,如果增加的总长度不超过预先申请的内存块的长度,就不用再申请内存。当增加后的字符串长度超过预先申请的内存时,CString先释放原先的内存,然后再重新申请一个更大的内存块。同样的,当字符串长度减少时,也不释放多出来的内存空间。而是等到积累到一定程度时,才一次性将多余的内存释放。 > 1 **CString实现的机制.** > CString是通过“引用”来管理串的,“引用”这个词我相信大家并不陌生,象Window内核对象、COM对象等都是通过引用来实现的。而CString也是通过这样的机制来管理分配的内存块。实际上CString对象只有一个指针成员变量,所以任何CString实例的长度只有4字节. > 即: int len = sizeof(CString);//len等于4 > 这个指针指向一个相关的引用内存块,如图: CString str("abcd"); > \_\_\_ > \_\_\_\_\_\_\_\_\_\_\_\_ | | > | | | | > | 0x04040404 | | | head部,为引用内存块相关信息 > |\_\_\_\_\_\_\_\_\_\_\_\_| | | > str |\_\_\_| > |'a'| 0x40404040 > |'b'| > |'c'| > |'d'| > | 0 | > > 正因为如此,一个这样的内存块可被多个CString所引用,例如下列代码: > CString str("abcd"); > CString a = str; > CString b(str); > CString c; > c = b; > 上面代码的结果是:上面四个对象(str,a,b,c)中的成员变量指针有相同的值,都为0x40404040.而这块内存块怎么知道有多少个CString引用它呢?同样,它也会记录一些信息。如被引用数,串长度,分配内存长度。 > 这块引用内存块的结构定义如下: > struct CStringData > \{ > long nRefs; //表示有多少个CString 引用它. 4 > int nDataLength; //串实际长度. 4 > int nAllocLength; //总共分配的内存长度(不计这头部的12字节). 4 > \}; > 由于有了这些信息,CString就能正确地分配、管理、释放引用内存块。 > 如果你想在调试程序的时候获得这些信息。可以在Watch窗口键入下列表达式: > (CStringData\*)((CStringData\*)(this->m\_pchData)-1)或 > (CStringData\*)((CStringData\*)(str.m\_pchData)-1)//str为指CString实例 > > 正因为采用了这样的好机制,使得CString在大量拷贝时,不仅效率高,而且分配内存少。 > > > **2 LPCTSTR 与 GetBuffer(int nMinBufLength)** > 这两个函数提供了与标准C的兼容转换。在实际中使用频率很高,但却是最容易出错的地方。这两个函数实际上返回的都是指针,但它们有何区别呢?以及调用它们后,幕后是做了怎样的处理过程呢? > (1) LPCTSTR 它的执行过程其实很简单,只是返回引用内存块的串地址。它是作为操作符重载提供的, > 所以在代码中有时可以隐式转换,而有时却需强制转制。如: > CString str; > const char\* p = (LPCTSTR)str; > //假设有这样的一个函数,Test(const char\* p);你就可以这样调用 > Test(str);//这里会隐式转换为LPCTSTR > (2) GetBuffer(int nMinBufLength) 它类似,也会返回一个指针,不过它有点差别,返回的是LPTSTR > (3) 这两者到底有何不同呢?我想告诉大家,其本质上完全不一样,一般说LPCTSTR转换后只应该当常量使用,或者做函数的入参;而GetBuffer(...)取出指针后,可以通过这个指针来修改里面的内容,或者做函数的入参。为什么呢?也许经常有这样的代码: > CString str("abcd"); > char\* p = (char\*)(const char\*)str; > p\[2\] = 'z'; > 其实,也许有这样的代码后,你的程序并没有错,而且程序也运行得挺好。但它却是非常危险的。再看 > CString str("abcd"); > CString test = str; > .... > char\* p = (char\*)(const char\*)str; > p\[2\] = 'z'; > strcpy(p, "akfjaksjfakfakfakj");//这下完蛋了 > 你知道此时,test中的值是多少吗?答案是"abzd".它也跟着改变了,这不是你所期望发生的。但为什么会这样呢?你稍微想想就会明白,前面说过,因为CString是指向引用块的,str与test指向同一块地方,当你p\[2\]='z'后,当然test也会随着改变。所以用它做LPCTSTR做转换后,你只能去读这块数据,千万别去改变它的内容。 > > 假如我想直接通过指针去修改数据的话,那怎样办呢?就是用GetBuffer(...).看下述代码: > CString str("abcd"); > CString test = str; > .... > char\* p = str.GetBuffer(20); > p\[2\] = 'z'; // 执行到此,现在test中值却仍是"abcd" > strcpy(p, "akfjaksjfakfakfakj"); // 执行到此,现在test中值还是"abcd" > 为什么会这样?其实GetBuffer(20)调用时,它实际上另外建立了一块新内块存,并分配20字节长度的buffer,而原来的内存块引用计数也相应减1. 所以执行代码后str与test是指向了两块不同的地方,所以相安无事。 > > (4) 不过这里还有一点注意事项:就是str.GetBuffer(20)后,str的分配长度为20,即指针p它所指向的buffer只有20字节长,给它赋值时,切不可超过,否则灾难离你不远了;如果指定长度小于原来串长度,如GetBuffer(1),实际上它会分配4个字节长度(即原来串长度);另外,当调用GetBuffer(...)后并改变其内容,一定要记得调用ReleaseBuffer(),这个函数会根据串内容来更新引用内存块的头部信息。 > (5) 最后还有一注意事项,看下述代码: > char\* p = NULL; > const char\* q = NULL; > \{ > CString str = "abcd"; > q = (LPCTSTR)str; > p = str.GetBuffer(20); > AfxMessageBox(q);// 合法的 > strcpy(p, "this is test");//合法的, > \} > AfxMessageBox(q);// 非法的,可能完蛋 > strcpy(p, "this is test");//非法的,可能完蛋 > 这里要说的就是,当返回这些指针后,如果CString对象生命结束,这些指针也相应无效。 > > > > 下面演示一段代码执行过程 > void Test() > \{ > CString str("abcd");//str指向一引用内存块(引用内存块的引用计数为1, > 长度为4,分配长度为4) > CString a;//a指向一初始数据状态, > a = str; //a与str指向同一引用内存块(引用内存块的引用计数为2, > 长度为4,分配长度为4) > CString b(a);//a、b与str指向同一引用内存块(引用内存块的引用 > 计数为3,长度为4,分配长度为4) > \{ > LPCTSTR temp = (LPCTSTR)a;//temp指向引用内存块的串首地址。 > (引用内存块的引用计数为3,长度为4,分配长度为4) > CString d = a; //a、b、d与str指向同一引用内存块(引用内存块的引用计数为4, 长度为4,分配长度为4) > b = "testa"; //这条语句实际是调用CString::operator=(CString&)函数。 > b指向一新分配的引用内存块。(新分配的引用内存块的 > 引用计数为1,长度为5,分配长度为5) > //同时原引用内存块引用计数减1. a、d与str仍指向原 > 引用内存块(引用内存块的引用计数为3,长度为4,分配长度为4) > \}//由于d生命结束,调用析构函数,导至引用计数减1(引用内存 > 块的引用计数为2,长度为4,分配长度为4) > LPTSTR temp = a.GetBuffer(10);//此语句也会导致重新分配新内存块。 > temp指向新分配引用内存块的串首地址(新 > 分配的引用内存块的引用计数为1,长度 > 为0,分配长度为10) > //同时原引用内存块引用计数减1. 只有str仍 > 指向原引用内存块(引用内存块的引用计数为1, > 长度为4,分配长度为4) > strcpy(temp, "temp"); //a指向的引用内存块的引用计数为1,长度为0,分配长度为10 > a.ReleaseBuffer();//注意:a指向的引用内存块的引用计数为1,长度为4,分配长度为10 > \} > //执行到此,所有的局部变量生命周期都已结束。对象str a b 各自调用自己的析构构 > //函数,所指向的引用内存块也相应减1 > //注意,str a b 所分别指向的引用内存块的计数均为0,这导致所分配的内存块释放 > > 通过观察上面执行过程,我们会发现CString虽然可以多个对象指向同一引用内块存,但是它们在进行各种拷贝、赋值及改变串内容时,它的处理是很智能并且非常安全的,完全做到了互不干涉、互不影响。当然必须要求你的代码使用正确恰当,特别是实际使用中会有更复杂的情况,如做函数参数、引用、及有时需保存到CStringList当中,如果哪怕有一小块地方使用不当,其结果也会导致发生不可预知的错误 > > > > 5 FreeExtra()的作用 > 看这段代码 > (1) CString str("test"); > (2) LPTSTR temp = str.GetBuffer(50); > (3) strcpy(temp, "there are 22 character"); > (4) str.ReleaseBuffer(); > (5) str.FreeExtra(); > 上面代码执行到第(4)行时,大家都知道str指向的引用内存块计数为1,长度为22,分配长度为50. 那么执行str.FreeExtra()时,它会释放所分配的多余的内存。(引用内存块计数为1,长度为22,分配长度为22) > > \------------------------------------------------------- > > CString GetBuffer() > > **LPTSTR GetBuffer( int** *nMinBufLength* **) 这个函数是CString 的一个比较实用的函数,请看如下示例:** > > GetBuffer(int nMinBufLength);的参数问题一直比较困扰人,网站的资料还像也不是太好给的,请看msdn解释 > > > **Parameters** > *nMinBufLength* > The minimum size of the character buffer in characters. This value does not include space for a null terminator. > 得到buffer的最小长度,当然这是由我们自己设定的一个参数,其原型定义如下: > 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; > \} > > 上面的代码已经比较清楚了,当设定的长度小于原字符串长度时,nMinBufLength = nOldLen,然后分配相应的内存,当你设定的长度大于原字符串本身的长度时就要分配一块比较大的空间出来,这时你可以实现字符串对接的操作,请看如下一段代码: > > > ![None.gif][] CString s; > ![None.gif][] s = " abc " ; > ![None.gif][] char \* p = s.GetBuffer( 6 ); > ![None.gif][] int l = s.GetLength(); > ![None.gif][] memcpy(p \+ l, " 111 " , 3 ); > ![None.gif][] char \* c = new char \[ 6 \]; > ![None.gif][] memset(c, ' 1 ' , 3 ); > ![None.gif][] char \* d = " sss " ; > ![None.gif][] strcat(c,d); > ![None.gif][] CString e; > ![None.gif][] e = (LPCTSTR)c; > ![None.gif][] AfxMessageBox(e); > ![None.gif][] AfxMessageBox(s); > ![None.gif][] > ![None.gif][] s.ReleaseBuffer(); // Surplus memory released, p is now invalid. > ![None.gif][] > ![None.gif][] > ![None.gif][] > > 这段代码主要是考察GetBuffer()函数的动作,在char\* p=s.GetBuffer(6),通过debug 可以看到p\[6\]='',这说明他自动加上了一个结束符,如果你分配空间为5这时将发生指针越界的现象,再次印证了原函数的动作。 > > > ![None.gif][] CString a( " hello world " ); > ![None.gif][] char \* b = a.GetBuffer( 0 ); > ![None.gif][]strcpy(b, " guanchanghui " ); > ![None.gif][]a.ReleaseBuffer(); > > > > \------------------------------------------------------- > > > > 6 Format(...) 与 FormatV(...) > 这条语句在使用中是最容易出错的。因为它最富有技巧性,也相当灵活。在这里,我没打算对它细细分析,实际上sprintf(...)怎么用,它就怎么用。我只提醒使用时需注意一点:就是它的参数的特殊性,由于编译器在编译时并不能去校验格式串参数与对应的变元的类型及长度。所以你必须要注意,两者一定要对应上, > 否则就会出错。如: > CString str; > int a = 12; > str.Format("first:%l, second: %s", a, "error");//result?试试 %l应改为%d > > 7 LockBuffer() 与 UnlockBuffer() > 顾名思议,这两个函数的作用就是对引用内存块进行加锁及解锁。 > 但使用它有什么作用及执行过它后对CString串有什么实质上的影响。其实挺简单,看下面代码: > (1) CString str("test"); > (2) str.LockBuffer(); > (3) CString temp = str; > (4) str.UnlockBuffer(); > (5) str.LockBuffer(); > (6) str = "error"; > (7) str.ReleaseBuffer(); > 执行完(3)后,与通常情况下不同,temp与str并不指向同一引用内存块。你可以在watch窗口用这个表达式(CStringData\*)((CStringData\*)(str.m\_pchData)-1)看看。 > 其实在msdn中有说明: > While in a locked state, the string is protected in two ways: > > No other string can get a reference to the data in the locked string, even if that string is assigned to the locked string. > The locked string will never reference another string, even if that other string is copied to the locked string. > > 8 CString 只是处理串吗? > 不对,CString不只是能操作串,而且还能处理内存块数据。功能完善吧!看这段代码 > char p\[20\]; > for(int loop=0; loop<sizeof(p); loop++) > \{ > p\[loop\] = 10-loop; > \} > CString str((LPCTSTR)p, 20); > char temp\[20\]; > memcpy(temp, str, str.GetLength()); > str完全能够转载内存块p到内存块temp中。所以能用CString来处理二进制数据 > > > > 8 AllocSysString()与SetSysString(BSTR\*) > 这两个函数提供了串与BSTR的转换。使用时须注意一点:当调用AllocSysString()后,须调用它SysFreeString(...) > > > > 9 参数的安全检验 > 在MFC中提供了多个宏来进行参数的安全检查,如:ASSERT. 其中在CString中也不例外,有许多这样的参数检验,其实这也说明了代码的安全性高,可有时我们会发现这很烦,也导致Debug与Release版本不一样,如有时程序Debug通正常,而Release则程序崩溃;而有时恰相反,Debug不行,Release行。其实我个人认为,我们对CString的使用过程中,应力求代码质量高,不能在Debug版本中出现任何断言框,哪怕release运行似乎 > 看起来一切正常。但很不安全。如下代码: > (1) CString str("test"); > (2) str.LockBuffer(); > (3) LPTSTR temp = str.GetBuffer(10); > (4) strcpy(temp, "error"); > (5) str.ReleaseBuffer(); > (6) str.UnlockBuffer();//执行到此时,Debug版本会弹出错框 > > > > 10 CString的异常处理 > 我只想强调一点:只有分配内存时,才有可能导致抛出CMemoryException. > 同样,在msdn中的函数声明中,注有throw( CMemoryException)的函数都有重新分配或调整内存的可能。 > > 由于数据结构比较复杂(使用CStringData),所以在使用的时候就出现了很多的问题,最典型的一个就是用来描述内存块属性的属性值和实际的值不一致。出现这个问题的原因就是CString为了方便某些应用,提供了一些operations,这些operation可以直接返回内存块中的字符串的地址值,用户可以通过对这个地址值指向的地址进行修改,但是,修改后又没有调用相应的operations1使CStringData中的值来保持一致。比如,用户可以首先通过operations得到字符串地址,然后将一些新的字符增加到这个字符串中,使得字符串的长度增加,但是,由于是直接通过指针修改的,所以描述该字符串长度的CStringData中的nDataLength却还是原来的长度,因此当通过GetLength获取字符串长度时,返回的必然是不正确的。 > 最后一点想法:写得这里,其实CString中还有许多技巧性的好东东,我并没去解释。如很多重载的操作符、查找等。我认为还是详细看看msdn,这样会比我讲好多了。我只侧重那些情况下会可能出错。当然,叙述中如有错误,敬请高手指点,不胜感谢! > > > > **附上一点CString的类型转化:** > > 1、CString to char\* > 经过类型强制转换,可以将CString类型转换成char\*,例如: > CString cStr = "Hello,world!"; > char\* zStr = (char\*)(LPCTSTR)cStr; > > 2、char\* to CString > char\*类型可以直接给CString,完成自动转换,例如: > char\* zStr = "Hello,world!"; > CString cStr = zStr; > > 3、CString to LPCSTR > 将CString转换成LPCSTR,需要获得CString的长度,例如: > CString cStr = \_T("Hello,world!"); > int nLen = cStr.GetLength(); > LPCSTR lpszBuf = cStr.GetBuffer(nLen); > > 4、CString to LPSTR > 这个和第3个技巧是一样的,例如: > CString cStr = \_T("Hello,world!"); > int nLen = str.GetLength(); > LPSTR lpszBuf = str.GetBuffer(nLen); > > 5、Char\[\] to int > 将字符串类型转换成整数型,可以使用atoi函数,例如: > char c\[10\]; > int n; > n = atoi(c); > > 6、Char\[\] to float > 和第5个技巧一样,使用atof()函数可以转换成float型,例如: > char c\[10\]; > float f; > f = atof(c); > > 7、Char\* to int > 和第5个技巧完全一样,例如: > char \*str = "100"; > int i; > i = atoi(str); > > > > ## ============================================ ## > > **CString,int,string,char\*之间的转换** > string 转 CString > CString.format(”%s”, string.c\_str()); > char 转 CString > CString.format(”%s”, char\*); > char 转 string > string s(char \*); > string 转 char \* > char \*p = string.c\_str(); > CString 转 string > string s(CString.GetBuffer()); > ***1,string -> CString*** > CString.format(”%s”, string.c\_str()); > 用c\_str()确实比**data**()要好. > ***2,char -> string*** > string s(char \*); > 你的只能初始化,在不是初始化的地方最好还是用assign(). > ***3,CString -> string*** > string s(CString.GetBuffer()); > GetBuffer()后一定要ReleaseBuffer(),否则就没有释放缓冲区所占的空间. > 《C++标准函数库》中说的 > 有三个函数可以将字符串的内容转换为字符数组和C—string > 1.data(),返回没有”\\0“的字符串数组 > 2,c\_str(),返回有”\\0“的字符串数组 > 3,copy() > ————————————————————— > CString与int、char\*、char\[100\]之间的转换- - > CString与int、char\*、char\[100\]之间的转换- - > > ***CString互转int*** > 将字符转换为整数,可以使用atoi、\_atoi64或atol。 > 而将数字转换为CString变量,可以使用CString的Format函数。如 > CString s; > int i = 64; > s.Format(”%d”, i) > Format函数的功能很强,值得你研究一下。 > void CStrDlg::OnButton1() > \{ > // TODO: Add your control notification handler code here > CString > ss=”1212.12″; > int temp=atoi(ss); > CString aa; > aa.Format(”%d”,temp); > AfxMessageBox(”var is ” + aa); > \} > sart.Format(”%s”,buf); > ***CString互转char\**** > ///char \* TO cstring > CString strtest; > char \* charpoint; > charpoint=”give string a value”; > strtest=charpoint; > ///cstring TO char \* > charpoint=strtest.GetBuffer(strtest.GetLength()); > 标准C里没有string,char \*==char \[\]==string > 可以用CString.Format(”%s”,char \*)这个方法来将char \*转成CString。要把CString转成char \*,用操作符(LPCSTR)CString就可以了。 > ***CString转换 char\[100\]*** > char a\[100\]; > CString str(”aaaaaa”); > strncpy(a,(LPCTSTR)str,sizeof(a)); > Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1570001 > > **(一) 概述** > string和CString均是字符串模板类,string为标准模板类(STL)定义的字符串类,已经纳入C++标准之中; > CString(typedef CStringT > CString)为Visual C++中最常用的字符串类,继承自CSimpleStringT类,主要应用在MFC和ATL编程中,主要数据类型有char(应用于ANSI),wchar\_t(unicode),TCHAR(ANSI与unicode均可); > char\*为C编程中最常用的字符串指针,一般以’\\0′为结束标志; > **(二) 构造** > string是方便的,可以从几乎所有的字符串构造而来,包括CString和char\*; > CString次之,可以从基本的一些字符串变量构造而来,包括char\*等; > char\*没有构造函数,仅可以赋值; > 举例: > char\* psz = “joise”; > CString cstr( psz ); > string str( cstr ); > **(三) 运算符重载** > a) perator= > string是最方便的,几乎可以直接用所有的字符串赋值,包括CString和char\*; > CString次之,可以直接用些基本的字符串赋值,包括char\*等; > char\*只能由指针赋值,并且是极危险的操作,建议使用strcpy或者memcpy,而且char\*在声明的时候如未赋初值建议先设为NULL,以避免野指针,令你抓狂; > 举例: > char \*psz = NULL; > psz = new char\[10\]; //当然,以上的直接写成char \*psz = new char\[10\];也是一样 > memset( psz, 0, 10 ); > strcpy( psz, “joise” ); > CString cstr; > cstr = psz; > string str; > str = psz; > str = cstr; > delete \[\]psz; > b) operator+ > string与CString差不多,可以直接与char\*进行加法,但不可以相互使用+运算符,即string str = str + cstr是非法的,须转换成char\*; > char\*没有+运算,只能使用strcat把两个指针连在一起; > 举例: > char\* psz = “joise”; > CString cstr = psz; > cstr = cstr + psz; > string str = psz; > str = str + str + psz; > strcat( psz, psz ); > strcat( psz, cstr );//合法 > strcat( psz, str );//非法,由此可见,CString可自动转换为const char\*,而string不行 > c) operator += > string是最强大的,几乎可以与所有的字符串变量+=,包括CString和char\*; > CString次之,可以与基本的一些字符串变量进行+=而来,包括char\*等; > char\*没有+=运算符,只能使用strcat把两个指针连在一起; > d) operator\[\] > CString最好,当越界时会抛出断言异常; > string与char\*下标越界结果未定义; > 举例: > char\* psz = “joise”; > CString cstr = psz; > cout << cstr\[8\]; > string str = psz; > cout << str\[8\]; > cout << psz\[8\]; > e) perator== 、operator!=、operator> 、operator< 、operator>= 、perator<= > CString与string之间不可以进行比较,但均可以与char\*进行比较,并且比较的是值,而不是地址; > cout << ( psz == cstr ); > cout << ( psz == str ); > cout << ( str == psz ); > cout << ( cstr == psz );//以上**代码**返回均为1 > **(四) 常用算法** > ***a) 查找*** > 作用 char\* string CString > 查找指定值 strchr > strstr > strrstr > strspn find Find > 第一个匹配的值 fild\_first\_of FindOneOf 从后面开始查找 ReserveFind 指定匹配方式 find\_if > 注:find\_if中是把范围内的值挨个代入匹配函数直至返回true > ***b) 比较*** > 作用 char\* string CString 查找指定值(区分大小写) strcmp > strncmp > strcoll > \_strncoll operator< > operator> > operator<= > operator>= > operator== > operator!= Collate > Compare 查找指定值(不区分大小写) \_stricmp > \_strnicmp > \_str > > \_strn > > 注:返回值如果<0则前面的值小于后面的值,反之亦然 > ***c) 替换*** > 作用 char\* string CString 查找指定值 \_strset > \_strnset > replace > replace\_copy > replace\_copy\_if > replace\_if Replace > ***d) 插入*** > 作用 char\* string CString 查找指定值 insert Insert > e) 增加 作用 char\* string CString 动态增加值 strcat push > append Append > AppendChar > AppendFormat > ***f) 截取*** > 作用 char\* string CString 得到部分值 用下标操作 substr Left > Mid > Right > Truncate > ***g) 移除*** > 作用 char\* string CString 移除部份值 remove Remove 移除空白值 RemoveBlanks > 注:此为ATL提供,非C函数 remove\_if Trim > TrimLeft > TrimRig > ***h) 转换大小写*** > 作用 char\* string CString 转换大小写 \_strlwr > \_strupr MakeLower > MakeUpper > **i) 与其他类型转换** > 作用 char\* string CString 转化为数字 atoi > atod > atof Format 转化为char\* c\_str > GetBuffer > GetBufferSetLen > **j) 格式化** > 作用 char\* string CString 格式化 sprintf Format > **k) 得到长度** > 作用 char\* string CString > 得到长度 strlen length GetLength 得到大小 size GetAllocLength > **l) 判断为空** > 作用 char\* string CString 判断是否为空 判断是否==NULL或者第一个字符是否是’\\0′ empty IsEmpty > **m) 重定义大小** > 作用 char\* string CString 重定义大小 realloc > new resize GetBufferSetLength > **n) 释放资源** > 作用 char\* string CString 释放 free > delete (delete\[\]) ReleaseBuffer > ReleaseBufferSetLength > ***(五)* *安全性**>*** > CString > string > char\*; > ***(六) 灵活性*** > CString > string >char\*; > ***(七) 可移植性*** > char\* = string > CString > [http_hi.baidu.com_luosiyong_item_df7a7e8f03f311d75e0ec10d]: http://hi.baidu.com/luosiyong/item/df7a7e8f03f311d75e0ec10d [SouthEast]: /images/20220612/1a454de3763e458380a938108fed6dce.png [91939fccf4fd8d7c01e928bb.jpg]: /images/20220612/56d67c721b8d4da5ade09c5972a09676.png [http_topic.csdn.net_u_20080608_21_12a02aab-ce19-4e9c-8e75-6f09da4144f3.html]: http://topic.csdn.net/u/20080608/21/12a02aab-ce19-4e9c-8e75-6f09da4144f3.html [None.gif]: /images/20220612/48933f0282ed4974b36c841db08eca7a.png
还没有评论,来说两句吧...