打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
VC知识库文章
它把 CString 强制类型转化成 LPCTSTR,也就是说先获得改字符串的地址,然后再强制类型转化成 LPTSTR,以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效!如果你试图获取数据,则不能这么做。

  如果你打算获取存储在控件中的数据,则方法稍有不同,例如,对某个 CTreeCtrl 使用 GetItem 方法,我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT,因此我可以这样写:

TVITEM tvi;// ... assorted initialization of other fields of tvitvi.pszText = s.GetBuffer(MY_LIMIT);tvi.cchTextMax = MY_LIMIT;c_MyTree.GetItem(&tvi);s.ReleaseBuffer();

  可以看出来,其实上面的代码对所有类型的 Set 方法都适用,但是并不需要这么做,因为所有的类 Set 方法(包括 Insert方法)不会改变字符串的内容。但是当你需要写 CString 对象时,必须保证缓冲是可写的,这正是 GetBuffer 所做的事情。再次强调: 一旦做了一次 GetBuffer 调用,那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。

5、CString 型转化成 BSTR 型

  当我们使用 ActiveX 控件编程时,经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串,Intel平台上的宽字符串(Unicode),并且 可以包含嵌入的 NULL 字符。

你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR:

CString s;s = ... ; // whateverBSTR b = s.AllocSysString();

  现在指针 b 指向的就是一个新分配的 BSTR 对象,该对象是 CString 的一个拷贝,包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常,BSTR 由接收它的组件来释放,如果你需要自己释放 BSTR 的话,可以这么做:

::SysFreeString(b);

  对于如何表示传递给 ActiveX 控件的字符串,在微软内部曾一度争论不休,最后 Visual Basic 的人占了上风,BSTR(“Basic String”的首字母缩写)就是这场争论的结果。

6、BSTR 型转化成 CString 型

  由于 BSTR 是记数 Unicode 字符串,你可以用标准转换方法来创建 8 位的 CString。实际上,这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode,也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串,VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。

例如,在一个ANSI程序中:

BSTR b;b = ...; // whateverCString s(b == NULL ? L"" : b)

  对于单个的 BSTR 串来说,这种用法可以工作得很好,这是因为 CString 有一个特殊的构造函数以LPCWSTR(BSTR正是这种类型) 为参数,并将它转化成 ANSI 类型。专门检查是必须的,因为 BSTR 可能为空值,而 CString 的构造函数对于 NULL 值情况考虑的不是很周到,(感谢 Brian Ross 指出这一点!)。这种用法也只能处理包含 NUL 终结字符的单字符串;如果要转化含有多个 NULL 字符 串,你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意,应该尽量避免。
  根据 C/C++ 规则,如果你有一个 LPWSTR,那么它别无选择,只能和 LPCWSTR 参数匹配。

在 Unicode 模式下,它的构造函数是:

CString::CString(LPCTSTR);

正如上面所表示的,在 ANSI 模式下,它有一个特殊的构造函数:

CString::CString(LPCWSTR); 

  它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。(在Unicode模式下,有一个专门的构造函数,该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串 指针,该函数将它加宽为 Unicode 的字符串!)再次强调:一定要检查 BSTR 的值是否为 NULL。
  另外还有一个问题,正如上文提到的:BSTRs可以含有多个内嵌的NULL字符,但是 CString 的构造函数只能处理某个串中单个 NULL 字符。 也就是说,如果串中含有嵌入的 NUL字节,CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数,你会发现 它们都调用了lstrlen,也就是计算字符串的长度。
  注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte,如果你不想使用这种默认的转换方式,则必须编写自己的转化代码。
  如果你在 UNICODE 模式下编译代码,你可以简单地写成:

CString convert(BSTR b){    if(b == NULL)        return CString(_T(""));    CString s(b); // in UNICODE mode    return s;}
  如果是 ANSI 模式,则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如,指定不同的默认字符,不同的标志集等。
CString convert(BSTR b){    CString s;    if(b == NULL)       return s; // empty for NULL BSTR#ifdef UNICODE    s = b;#else    LPSTR p = s.GetBuffer(SysStringLen(b) + 1);     ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page                          0,                 // no flags                          b,                 // source widechar string                          -1,                // assume NUL-terminated                          p,                 // target buffer                          SysStringLen(b)+1, // target buffer length                          NULL,              // use system default char                          NULL);             // don‘‘t care if default used    s.ReleaseBuffer();#endif    return s;}
  我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么,因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。

7、VARIANT 型转化成 CString 型

  事实上,我从来没有这么做过,因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert Quirk 的一篇帖子谈到了这种转化,我觉得把他的文章包含在我的文章里是不太好的做法,所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。
  VARIANT 类型经常用来给 COM 对象传递参数,或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法,函数返回什么类型 依赖可能(并且常常)方法的输入参数(比如,在自动化操作中,依赖与你调用哪个方法。IDispatch::Invoke 可能返回(通过其一个参数)一个 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果,(详见 MSDN 上的 VARIANT 结构的定义)。在下面的例子中,假设 类型是一个BSTR的变体,也就是说在串中的值是通过 bsrtVal 来引用,其优点是在 ANSI 应用中,有一个构造函数会把 LPCWCHAR 引用的值转换为一个 CString(见 BSTR-to-CString 部分)。在 Unicode 模式中,将成为标准的 CString 构造函数,参见对缺省::WideCharToMultiByte 转换的告诫,以及你觉得是否可以接受(大多数情况下,你会满意的)。
VARIANT vaData;vaData = m_com.YourMethodHere();ASSERT(vaData.vt == VT_BSTR);CString strData(vaData.bstrVal);

你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑:

CString VariantToString(VARIANT * va){    CString s;    switch(va->vt)      { /* vt */       case VT_BSTR:          return CString(vaData->bstrVal);       case VT_BSTR | VT_BYREF:          return CString(*vaData->pbstrVal);       case VT_I4:          s.Format(_T("%d"), va->lVal);          return s;       case VT_I4 | VT_BYREF:          s.Format(_T("%d"), *va->plVal);       case VT_R8:          s.Format(_T("%f"), va->dblVal);          return s;       ... 剩下的类型转换由读者自己完成       default:          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)          return CString("");      } /* vt */}
8、载入字符串表资源

  如果你想创建一个容易进行语言版本移植的应用程序,你就不能在你的源代码中直接包含本土语言字符串 (下面这些例子我用的语言都是英语,因为我的本土语是英语),比如下面这种写法就很糟:
CString s = "There is an error";

  你应该把你所有特定语言的字符串单独摆放(调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:

s.Format(_T("%d - %s"), code, text);

  在你的程序中,文字字符串不是语言敏感的。不管怎样,你必须很小心,不要使用下面这样的串:

// fmt is "Error in %s file %s"// readorwrite is "reading" or "writing"s.Format(fmt, readorwrite, filename); 

  这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误,尽管我懂德语,知道在德语的语法中动词放在句子的最后面,我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法(也是我现在使用的办法)是使用两个字符串,一个用 于读,一个用于写,在使用时加载合适的版本,使得它们对字符串参数是非敏感的。也就是说加载整个格式,而不是加载串“reading”,“writing”:

// fmt is "Error in reading file %s"// "Error in writing file %s"s.Format(fmt, filename);

  一定要注意,如果你有好几个地方需要替换,你一定要保证替换后句子的结构不会出现问题,比如在英语中,可以是主语-宾语,主语-谓语,动词-宾语的结构等等。
  在这里,我们并不讨论 FormatMessage,其实它比 sprintf/Format 还要有优势,但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字,这样在你输出的时候就不会把他们的位置排错了。
  接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下:首先使用 Visual Studio 的资源编辑器创建一个字符串,然后给每一个字符串取一个ID,一般我们给它取名字都以 IDS_开头。所以如果你有一个信息,你可以创建一个字符串资源然后取名为 IDS_READING_FILE,另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中:

STRINGTABLEIDS_READING_FILE "Reading file %s"IDS_WRITING_FILE "Writing file %s"END

注意:这些资源都以 Unicode 的格式保存,不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在,虽然 Win9x 不能真正处理 Unicode。
然后你可以这样使用这些资源:
// 在使用资源串表之前,程序是这样写的:

   CString fmt;      if(...)        fmt = "Reading file %s";     else       fmt = "Writing file %s";  ...    // much later  CString s;  s.Format(fmt, filename); 
// 使用资源串表之后,程序这样写:
    CString fmt;        if(...)           fmt.LoadString(IDS_READING_FILE);        else           fmt.LoadString(DS_WRITING_FILE);    ...      // much later    CString s;    s.Format(fmt, filename);
  现在,你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数,然后它从 STRINGTABLE 中取出它对应的字符串,赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出,但是在 构造函数的示例程序中使用了。(为什么这个特性没有成为正式文档的一部分,而是放在了一个例子中,我记不得了!)——【译者注:从这句话看,作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是:如果你将一个字符串资源的ID强制类型转换为 LPCTSTR,将会隐含调用 LoadString。因此,下面两个构造字符串的例子具有相同的效果,而且其 ASSERT 在debug模式下不会被触发:
CString s;s.LoadString(IDS_WHATEVER);CString t( (LPCTSTR)IDS_WHATEVER );ASSERT(s == t);//不会被触发,说明s和t是相同的。

  现在,你可能会想:这怎么可能工作呢?我们怎么能把 STRINGTABLE ID 转化成一个指针呢?很简单:所有的字符串 ID 都在1~65535这个范围内,也就是说,它所有的高位都是0,而我们在程序中所使用的指针是不可能小于65535的,因为程序的低 64K 内存永远也不可能存在的,如果你试图访问0x00000000到0x0000FFFF之间的内存,将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址,所以我们可以用这些值来作为字符串资源的ID。
  我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住,大多数的方法即可以接受一个 UINT 型的参数,也可以接受一个 LPCTSTR 型的参数,这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样,你也可以给很多种结构只传递一个资源名。

CString s;s.LoadString(IDS_WHATEVER);CString t( MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);

  告诉你吧:我不仅只是在这里鼓吹,事实上我也是这么做的。在我的代码中,你几乎不可能找到一个字符串,当然,那些只是偶然在调试中出现的或者和语言无关的字符串除外。

9、CString 和临时对象

  这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题,我简单的提一下,这个问题是有个程序员需要往注册表中写入一个字符串,他写道:
  我试着用 RegSetValueEx() 设置一个注册表键的值,但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作,但是当我用 CString 的时候,总是得到一些垃圾:"ÝÝÝÝ...ÝÝÝÝÝÝ"为了确认是不是我的 CString 数据出了问题,我试着用 GetBuffer,然后强制转化成 char*,LPCSTR。GetBuffer 返回的值是正确的,但是当我把它赋值给 char* 时,它就变成垃圾了。以下是我的程序段:

char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey, "Name", 0, REG_SZ,              (CONST BYTE *) szName,             strlen (szName + 1));

这个 Name 字符串的长度小于 20,所以我不认为是 GetBuffer 的参数的问题。

真让人困惑,请帮帮我。

亲爱的 Frustrated,

你犯了一个相当微妙的错误,聪明反被聪明误,正确的代码应该象下面这样:

CString Name = GetName();RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,                     (CONST BYTE *) (LPCTSTR)Name,                    (Name.GetLength() + 1) * sizeof(TCHAR));
  为什么我写的代码能行而你写的就有问题呢?主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见:《C++ Reference manual》§12.2
  在一些环境中,编译器有必要创建一个临时对象,这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话,编译器要确保这个类的构造函数被调用。同样的,如果这个类声明有析构函数的话,也要保证这个临时对象的析构函数被调用。
  编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。
  大部分的编译器是这样设计的:在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数,实现起来,一般都是在下一个分号处。因此,这个 CString 对象在 GetBuffer 调用之后就被析构了(顺便提一句,你没有理由给 GetBuffer 函数传递一个参数,而且没有使用ReleaseBuffer 也是不对的)。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针,但是当这个临时对象被析构后,这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD,显示出来刚好就是“Ý”符号。在这个时候你向注册表中写数据,字符串的内容当然全被破坏了。
  我们不应该立即把这个临时对象转化成 char* 类型,应该先把它保存到一个 CString 对象中,这意味着把临时对象复制了一份,所以当临时的 CString 对象被析构了之后,这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。
  此外,我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小,使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半,而且它也不能从这个字符串的第二个字符起开始计算,也许你的本意是 lstrlen(Name) + 1(OK,我承认,我也犯了同样的错误!)。不论如何,在 Unicode 模式下,所有的字符都是2个字节大小,我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默:REG_SZ 的值究竟是以字节计算还是以字符计算呢?我们假设它指的是以字节为单位计算,你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。

10、CString 的效率

  CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲,它也确实可以被实现得更加高效,你可能会说下面的代码:
CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;

比起下面的代码来,效率要低多了:

char s[1024];lstrcpy(s, SomeString1);lstrcat(s, SomeString2);lstrcat(s, SomeString 3);lstrcat(s, ",");lstrcat(s, SomeString4);

  总之,你可能会想,首先,它为 SomeCString1 分配一块内存,然后把 SomeCString1 复制到里面,然后发现它要做一个连接,则重新分配一块新的足够大的内存,大到能够放下当前的字符串加上SomeCString2,把内容复制到这块内存 ,然后把 SomeCString2 连接到后面,然后释放第一块内存,并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上,在很多情况下根本就不需要复制源字符串(在 += 操作符左边的字符串)。
  在 VC++6.0 中,Release 模式下,所有的 CString 中的缓存都是按预定义量子分配的。所谓量子,即确定为 64、128、256 或者 512 字节。这意味着除非字符串非常长,连接字符串的操作实际上就是 strcat 经过优化后的版本(因为它知道本地的字符串应该在什么地方结束,所以不需要寻找字符串的结尾;只需要把内存中的数据拷贝到指定的地方即可)加上重新计算字符串的长度。所以它的执行效率和纯 C 的代码是一样的,但是它更容易写、更容易维护和更容易理解。
  如果你还是不能确定究竟发生了怎样的过程,请看看 CString 的源代码,strcore.cpp,在你 vc98的安装目录的 mfc\src 子目录中。看看 ConcatInPlace 方法,它被在所有的 += 操作符中调用。

啊哈!难道 CString 真的这么"高效"吗?比如,如果我创建

CString cat("Mew!");

  然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区(4个字符加一个结束字符),系统将给我分配64个字节,而其中59个字节都被浪费了。
  如果你也是这么想的话,那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错,这种说法的确正确,但是他忽略了事实中一个很重要的方面。
  如果你编写的是运行在16K EPROMs下的嵌入式程序的话,你有理由尽量少使用空间,在这种环境下,它能使你的程序更健壮。但是在 500MHz, 256MB的机器上写 Windows 程序,如果你还是这么做,它只会比你认为的“低效”的代码运行得更糟。
  举例来说。字符串的大小被认为是影响效率的首要因素,使字符串尽可能小可以提高效率,反之则降低效率,这是大家一贯的想法。但是这种想法是不对的,精确的内存分配的后果要在程序运行了好几个小时后才能体现得出来,那时,程序的堆中将充满小片的内存,它们太小以至于不能用来做任何事,但是他们增加了你程序的内存用量,增加了内存页面交换的次数,当页面交换的次数增加到系统能够忍受的上限,系统则会为你的程序分配更多的页面,直到你的程序占用了所有的可用内存。由此可见,虽然内存碎片是决定效率的次要因素,但正是这些因素实际控制了系统的行为,最终,它损害了系统的可靠性,这是令人无法接受的。
  记住,在 debug 模式下,内存往往是精确分配的,这是为了更好的排错。
  假设你的应用程序通常需要连续工作好几个月。比如,我常打开 VC++,Word,PowerPoint,Frontpage,Outlook Express,Forté Agent,Internet Explorer和其它的一些程序,而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天(反之,如果你不幸不得不使用像 Adobe FrameMaker 这样的程序的话,你将会体会到可靠性的重要;这个程序机会每天都要崩溃4~6次,每次都是因为用完了所有的空间并填满我所有的交换页面)。所以精确内存分配是不可取的,它会危及到系统的可靠性,并引起应用程序崩溃。
  按量子的倍数为字符串分配内存,内存分配器就可以回收用过的内存块,通常这些回收的内存块马上就可以被其它的 CString 对象重新用到,这样就可以保证碎片最少。分配器的功能加强了,应用程序用到的内存就能尽可能保持最小,这样的程序就可以运行几个星期或几个月而不出现问题。
  题外话:很多年以前,我们在 CMU 写一个交互式系统的时候,一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim Mitchell,现在他在 Sun Microsystems 工作,那时侯他创造了一种内存分配器,它保留了一个内存分配状况的运行时统计表,这种技术和当时的主流分配器所用的技术都不同,且较为领先。当一个内存块需要被分割得比某一个值小的话,他并不分割它,因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针,他认为:与其让指令做长时间的存取内存操作,还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是对的。
  永远不要认为所谓的“最优化”是建立在每一行代码都高速且节省内存的基础上的,事实上,高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上,只使用最小内存的字符串分配策略可能是最糟糕的一种方法。
  如果你认为优化是你在每一行代码上做的那些努力的话,你应该想一想:在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》。
  记住,+= 运算符只是一种特例,如果你写成下面这样:

CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;

则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。

总结

  以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类,但是 MFC 没有很明显的指出这些特征,需要你自己去探索、去发现。




最新评论 [发表评论] [文章投稿]
查看所有评论
推荐给好友
打印

翻译的很好 以后再接再厉 多多翻译好作品。 ( flyingleaf 发表于 2004-4-30 13:23:00)
 
excellent ( Jerry_知识库 发表于 2004-4-26 9:32:00)
 
非常精彩! ( 远帆 发表于 2004-4-15 13:11:00)
 
www.codeproject.com上有很多类似的文章。 ( xiaojin 发表于 2004-4-15 12:24:00)
 
此类总结性文章非常好!
谢谢! ( ligang 发表于 2004-4-15 9:22:00)
 
欢迎加盟中国微型软件开发中心,网站正建设中。。。
为你量身定做各种小型软件,
http://minisoft.91x.net/
联系方式:
email:ldh5118@sohu.com
qq   : 36201365 ( rbird5118 发表于 2004-4-14 15:54:00)
 
.......................................................
More...


版权所有 ? 2004 VC知识库 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
vc字符串转换处理:(绝对精华,收集所有的例子)
有关数据类型转换
字符串转换 BSTR/LPSTR/LPWSTR/Char
VC常用数据类型使用转换详解
Windows字符串类型 CHAR* WCHAR* LPSTR LPCSTR
(const) char *转CString 详解(字符串类型转换详解,不断补充)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服