[VC++]CString转化成char

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()   来包围格式化字符串,但是基本   思路仍然是在走弯路,这这样很容易出错。

 

原文地址:https://www.cnblogs.com/boneking/p/1337793.html