安徽芜湖十大名小吃:CString 型和 char* 类型的相互转化

来源:百度文库 编辑:偶看新闻 时间:2024/06/12 14:51:18
http://topic.csdn.net/t/20050613/12/4078380.htmlCString   型和   char*   类型的相互转化 
    这是初学者使用   CString   时最常见的问题。有了   C++   的帮助,很多问题你不需要深入的去考虑它,直接拿来用就行了,但是如果你不能深入了解它的运行机制,又会有很多问题让你迷惑,特别是有些看起来没有问题的代码,却偏偏不能正常工作。 
比如,你会奇怪为什么不能写向下面这样的代码呢: 
CString   graycat   =   "Gray "   +   "Cat "; 
或者这样: 
CString   graycat( "Gray "   +   "Cat "); 
  事实上,编译器将抱怨上面的这些尝试。为什么呢?因为针对CString   和   LPCTSTR数据类型的各种各样的组合,“   +”   运算符   被定义成一个重载操作符。而不是两个   LPCTSTR   数据类型,它是底层数据类型。你不能对基本数据(如   int、char   或者   char*)类型重载   C++   的运算符。你可以象下面这样做: 
CString   graycat   =   CString( "Gray ")   +   CString( "Cat "); 
或者这样: 
CString   graycat   =   CString( "Gray ")   +   "Cat "; 
研究一番就会发现:“   +”总是使用在至少有一个   CString   对象和一个   LPCSTR   的场合。 
注意,编写有   Unicode   意识的代码总是一件好事,比如: 
CString   graycat   =   CString(_T( "Gray "))   +   _T( "Cat "); 
这将使得你的代码可以直接移植。 
char*   转化为   CString 
现在你有一个   char*   类型的数据,或者说一个字符串。怎么样创建   CString   对象呢?这里有一些例子: 
char   *   p   =   "This   is   a   test "; 
或者象下面这样更具有   Unicode   意识: 
TCHAR   *   p   =   _T( "This   is   a   test ") 
或 
LPTSTR   p   =   _T( "This   is   a   test "); 
你可以使用下面任意一种写法: 
CString   s   =   "This   is   a   test ";   //   8-bit   only 
CString   s   =   _T( "This   is   a   test ");   //   Unicode-aware 
CString   s( "This   is   a   test ");   //   8-bit   only 
CString   s(_T( "This   is   a   test "));   //   Unicode-aware 
CString   s   =   p; 
CString   s(p); 
  用这些方法可以轻松将常量字符串或指针转换成   CString。需要注意的是,字符的赋值总是被拷贝到   CString   对象中去的,所以你可以象下面这样操作: 
TCHAR   *   p   =   _T( "Gray "); 
CString   s(p); 
p   =   _T( "Cat "); 
s   +=   p; 
结果字符串肯定是“GrayCat”。 
CString   类还有几个其它的构造函数,但是这里我们不考虑它,如果你有兴趣可以自己查看相关文档。 
事实上,CString   类的构造函数比我展示的要复杂,比如: 
CString   s   =   "This   is   a   test ";   
  这是很草率的编码,但是实际上它在   Unicode   环境下能编译通过。它在运行时调用构造函数的   MultiByteToWideChar   操作将   8   位字符串转换成   16   位字符串。不管怎样,如果   char   *   指针是网络上传输的   8   位数据,这种转换是很有用的。 
CString   转化成   char*   之一:强制类型转换为   LPCTSTR; 
这是一种略微硬性的转换,有关“正确”的做法,人们在认识上还存在许多混乱,正确的使用方法有很多,但错误的使用方法可能与正确的使用方法一样多。 
  我们首先要了解   CString   是一种很特殊的   C++   对象,它里面包含了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。   有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串结尾有一个NULL字符)。字符记数和缓冲区长度被巧妙隐藏。 
  除非你做一些特殊的操作,否则你不可能知道给CString对象分配的缓冲区的长度。这样,即使你获得了该0缓冲的地址,你也无法更改其中的内容,不能截短字符串,也   绝对没有办法加长它的内容,否则第一时间就会看到溢出。 
  LPCTSTR   操作符(或者更明确地说就是   TCHAR   *   操作符)在   CString   类中被重载了,该操作符的定义是返回缓冲区的地址,因此,如果你需要一个指向   CString   的   字符串指针的话,可以这样做: 
CString   s( "GrayCat "); 
LPCTSTR   p   =   s; 
  它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时,C++规测容许这种选择。比如,你可以将(浮点数)定义为将某个复数   (有一对浮点数)进行强制类型转换后只返回该复数的第一个浮点数(也就是其实部)。可以象下面这样: 
Complex   c(1.2f,   4.8f); 
float   realpart   =   c; 
如果(float)操作符定义正确的话,那么实部的的值应该是1.2。 
  这种强制转化适合所有这种情况,例如,任何带有   LPCTSTR   类型参数的函数都会强制执行这种转换。   于是,你可能有这样一个函数(也许在某个你买来的DLL中): 
BOOL   DoSomethingCool(LPCTSTR   s); 
你象下面这样调用它: 
CString   file( "c:\\myfiles\\coolstuff ") 
BOOL   result   =   DoSomethingCool(file); 
  它能正确运行。因为   DoSomethingCool   函数已经说明了需要一个   LPCTSTR   类型的参数,因此   LPCTSTR   被应用于该参数,在   MFC   中就是返回的串地址。 
如果你要格式化字符串怎么办呢? 
CString   graycat( "GrayCat "); 
CString   s; 
s.Format( "Mew!   I   love   %s ",   graycat); 
  注意由于在可变参数列表中的值(在函数说明中是以“...”表示的)并没有隐含一个强制类型转换操作符。你会得到什么结果呢? 
  一个令人惊讶的结果,我们得到的实际结果串是: 
"Mew!   I   love   GrayCat "。 
  因为   MFC   的设计者们在设计   CString   数据类型时非常小心,   CString   类型表达式求值后指向了字符串,所以这里看不到任何象   Format   或   sprintf   中的强制类型转换,你仍然可以得到正确的行为。描述   CString   的附加数据实际上在   CString   名义地址之后。 
  有一件事情你是不能做的,那就是修改字符串。比如,你可能会尝试用“,”代替“.”(不要做这样的,如果你在乎国际化问题,你应该使用十进制转换的   National   Language   Support   特性,),下面是个简单的例子: 
CString   v( "1.00 ");   //   货币金额,两位小数 
LPCTSTR   p   =   v; 
p[lstrlen(p)   -   3]   =   ' ', ' '; 
  这时编译器会报错,因为你赋值了一个常量串。如果你做如下尝试,编译器也会错: 
strcat(p,   "each "); 
  因为   strcat   的第一个参数应该是   LPTSTR   类型的数据,而你却给了一个   LPCTSTR。 
不要试图钻这个错误消息的牛角尖,这只会使你自己陷入麻烦! 
  原因是缓冲有一个计数,它是不可存取的(它位于   CString   地址之下的一个隐藏区域),如果你改变这个串,缓冲中的字符计数不会反映所做的修改。此外,如果字符串长度恰好是该字符串物理限制的长度(梢后还会讲到这个问题),那么扩展该字符串将改写缓冲以外的任何数据,那是你无权进行写操作的内存(不对吗?),你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。 
CString转化成char*   之二:使用   CString   对象的   GetBuffer   方法; 
如果你需要修改   CString   中的内容,它有一个特殊的方法可以使用,那就是   GetBuffer,它的作用是返回一个可写的缓冲指针。   如果你只是打算修改字符或者截短字符串,你完全可以这样做: 
CString   s(_T( "File.ext ")); 
LPTSTR   p   =   s.GetBuffer(); 
LPTSTR   dot   =   strchr(p,   ' '. ' ');   //   OK,   should   have   used   s.Find... 
if(p   !=   NULL) 
*p   =   _T( ' '\0 ' '); 
s.ReleaseBuffer(); 
  这是   GetBuffer   的第一种用法,也是最简单的一种,不用给它传递参数,它使用默认值   0,意思是:“给我这个字符串的指针,我保证不加长它”。当你调用   ReleaseBuffer   时,字符串的实际长度会被重新计算,然后存入   CString   对象中。 
  必须强调一点,在   GetBuffer   和   ReleaseBuffer   之间这个范围,一定不能使用你要操作的这个缓冲的   CString   对象的任何方法。因为   ReleaseBuffer   被调用之前,该   CString   对象的完整性得不到保障。研究以下代码: 
CString   s(...); 
LPTSTR   p   =   s.GetBuffer(); 
//...   这个指针   p   发生了很多事情 
int   n   =   s.GetLength();   //   很糟D!!!!!   有可能给出错误的答案!!! 
s.TrimRight();   //   很糟!!!!!   不能保证能正常工作!!!! 
s.ReleaseBuffer();   //   现在应该   OK 
int   m   =   s.GetLength();   //   这个结果可以保证是正确的。 
s.TrimRight();   //   将正常工作。 
  假设你想增加字符串的长度,你首先要知道这个字符串可能会有多长,好比是声明字符串数组的时候用: 
char   buffer[1024]; 
表示   1024   个字符空间足以让你做任何想做得事情。在   CString   中与之意义相等的表示法: 
LPTSTR   p   =   s.GetBuffer(1024); 
  调用这个函数后,你不仅获得了字符串缓冲区的指针,而且同时还获得了长度至少为   1024   个字符的空间(注意,我说的是“字符”,而不是“字节”,因为   CString   是以隐含方式感知   Unicode   的)。 
  同时,还应该注意的是,如果你有一个常量串指针,这个串本身的值被存储在只读内存中,如果试图存储它,即使你已经调用了   GetBuffer   ,并获得一个只读内存的指针,存入操作会失败,并报告存取错误。我没有在   CString   上证明这一点,但我看到过大把的   C   程序员经常犯这个错误。 
  C   程序员有一个通病是分配一个固定长度的缓冲,对它进行   sprintf   操作,然后将它赋值给一个   CString: 
char   buffer[256]; 
sprintf(buffer,   "%...... ",   args,   ...);   //   ...   部分省略许多细节 
CString   s   =   buffer; 
虽然更好的形式可以这么做: 
CString   s; 
s.Format(_T( "%.... "),   args,   ...); 
如果你的字符串长度万一超过   256   个字符的时候,不会破坏堆栈。 
另外一个常见的错误是:既然固定大小的内存不工作,那么就采用动态分配字节,这种做法弊端更大: 
int   len   =   lstrlen(parm1)   +   13     lstrlen(parm2)   +   10   +   100; 
char   *   buffer   =   new   char[len]; 
sprintf(buffer,   "%s   is   equal   to   %s,   valid   data ",   parm1,   parm2); 
CString   s   =   buffer; 
...... 
delete   []   buffer; 
它可以能被简单地写成: 
CString   s; 
s.Format(_T( "%s   is   equal   to   %s,   valid   data "),   parm1,   parm2); 
  需要注意   sprintf   例子都不是   Unicode   就绪的,尽管你可以使用   tsprintf   以及用   _T()   来包围格式化字符串,但是基本   思路仍然是在走弯路,这这样很容易出错。