﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-时间的痕迹-文章分类-数据类型</title><link>http://www.cppblog.com/ivenher/category/175.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 03:38:19 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 03:38:19 GMT</pubDate><ttl>60</ttl><item><title>VC常用数据类型使用转换详解</title><link>http://www.cppblog.com/ivenher/articles/2205.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Wed, 28 Dec 2005 03:33:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/2205.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/2205.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/2205.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/2205.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/2205.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
<TBODY>
<TR>
<TD align=left width="2%" bgColor=#efefef></TD>
<TD align=left width="83%" bgColor=#efefef><BR>文章标题：<B>VC常用数据类型使用转换详解</B><BR>原 作 者：程佩君<BR>原 出 处：PCVC.NET<BR>发 布 者：loose_went<BR>发布类型：转载<BR>发布日期：2004-03-18<BR>今日浏览：7<BR>总 浏 览：537<BR></TD>
<TD vAlign=bottom align=left width="15%" bgColor=#efefef></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="96%" align=center border=0>
<TBODY>
<TR>
<TD vAlign=top align=left bgColor=#ffffff><BR>刚接触VC编程的朋友往往对许多数据类型的转换感到迷惑不解，本文将介绍一些常用数据类型的使用。 <BR><BR>我们先定义一些常见类型变量借以说明 <BR><BR>int i = 100; <BR>long l = 2001; <BR>float f=300.2; <BR>double d=12345.119; <BR>char username[]="女侠程佩君"; <BR>char temp[200]; <BR>char *buf; <BR>CString str; <BR>_variant_t v1; <BR>_bstr_t v2; <BR><BR>一、其它数据类型转换为字符串 <BR><BR>短整型(int) <BR>itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制 <BR>itoa(i,temp,2); ///按二进制方式转换 <BR>长整型(long) <BR>ltoa(l,temp,10); <BR><BR><BR>二、从其它包含字符串的变量中获取指向该字符串的指针 <BR><BR>CString变量 <BR>str = "2008北京奥运"; <BR>buf = (LPSTR)(LPCTSTR)str; <BR>BSTR类型的_variant_t变量 <BR>v1 = (_bstr_t)"程序员"; <BR>buf = _com_util::ConvertBSTRToString((_bstr_t)v1); <BR><BR>三、字符串转换为其它数据类型 <BR>strcpy(temp,"123"); <BR><BR>短整型(int) <BR>i = atoi(temp); <BR>长整型(long) <BR>l = atol(temp); <BR>浮点(double) <BR>d = atof(temp); <BR><BR>四、其它数据类型转换到CString <BR>使用CString的成员函数Format来转换,例如: <BR><BR>整数(int) <BR>str.Format("%d",i); <BR>浮点数(float) <BR>str.Format("%f",i); <BR>字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值 <BR>str = username; <BR><BR>五、BSTR、_bstr_t与CComBSTR <BR><BR>CComBSTR、_bstr_t是对BSTR的封装,BSTR是指向字符串的32位指针。 <BR>char *转换到BSTR可以这样: BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上头文件comutil.h <BR>反之可以使用char *p=_com_util::ConvertBSTRToString(b); <BR><BR><BR>六、VARIANT 、_variant_t 与 COleVariant <BR><BR>VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。 <BR>对于VARIANT变量的赋值：首先给vt成员赋值，指明数据类型，再对联合结构中相同数据类型的变量赋值，举个例子： <BR>VARIANT va; <BR>int a=2001; <BR>va.vt=VT_I4;///指明整型数据 <BR>va.lVal=a; ///赋值 <BR><BR>对于不马上赋值的VARIANT，最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系: <BR><BR>unsigned char bVal; VT_UI1 <BR>short iVal; VT_I2 <BR>long lVal; VT_I4 <BR>float fltVal; VT_R4 <BR>double dblVal; VT_R8 <BR>VARIANT_BOOL boolVal; VT_BOOL <BR>SCODE scode; VT_ERROR <BR>CY cyVal; VT_CY <BR>DATE date; VT_DATE <BR>BSTR bstrVal; VT_BSTR <BR>IUnknown FAR* punkVal; VT_UNKNOWN <BR>IDispatch FAR* pdispVal; VT_DISPATCH <BR>SAFEARRAY FAR* parray; VT_ARRAY|* <BR>unsigned char FAR* pbVal; VT_BYREF|VT_UI1 <BR>short FAR* piVal; VT_BYREF|VT_I2 <BR>long FAR* plVal; VT_BYREF|VT_I4 <BR>float FAR* pfltVal; VT_BYREF|VT_R4 <BR>double FAR* pdblVal; VT_BYREF|VT_R8 <BR>VARIANT_BOOL FAR* pboolVal; VT_BYREF|VT_BOOL <BR>SCODE FAR* pscode; VT_BYREF|VT_ERROR <BR>CY FAR* pcyVal; VT_BYREF|VT_CY <BR>DATE FAR* pdate; VT_BYREF|VT_DATE <BR>BSTR FAR* pbstrVal; VT_BYREF|VT_BSTR <BR>IUnknown FAR* FAR* ppunkVal; VT_BYREF|VT_UNKNOWN <BR>IDispatch FAR* FAR* ppdispVal; VT_BYREF|VT_DISPATCH <BR>SAFEARRAY FAR* FAR* pparray; VT_ARRAY|* <BR>VARIANT FAR* pvarVal; VT_BYREF|VT_VARIANT <BR>void FAR* byref; VT_BYREF <BR><BR>_variant_t是VARIANT的封装类，其赋值可以使用强制类型转换，其构造函数会自动处理这些数据类型。 <BR>例如： <BR>long l=222; <BR>ing i=100; <BR>_variant_t lVal(l); <BR>lVal = (long)i; <BR><BR>COleVariant的使用与_variant_t的方法基本一样，请参考如下例子： <BR>COleVariant v3 = "字符串", v4 = (long)1999; <BR>CString str =(BSTR)v3.pbstrVal; <BR>long i = v4.lVal; <BR><BR>七、其它 <BR><BR>对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如： <BR>LPARAM lParam; <BR>WORD loValue = LOWORD(lParam);///取低16位 <BR>WORD hiValue = HIWORD(lParam);///取高16位 <BR>对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如: <BR>WORD wValue; <BR>BYTE loValue = LOBYTE(wValue);///取低8位 <BR>BYTE hiValue = HIBYTE(wValue);///取高8位 <BR>后记：本文匆匆写成，错误之处在所难免，欢迎来信指正。 <BR><BR><BR>网友对该文章的评论 <BR>网友: catch(hw.wh.cn@163.net) 发表于: 2003-4-27 20:27:05 <BR><BR>我以前大学时学过C语言，但工作后就一直没用了(大概有两年了)，现在想从头学，请问我可以直接学习vc++吗？如果可以，请问我有什么地方要注意的吗？ <BR><BR>我想请您告诉我您的邮箱地址和您的QQ号码，这样我有问题就可以向您请教了，好吗？ <BR>如同意，可发到我的邮箱：hw.wh.cn@163.net <BR>谢谢！ <BR><BR>网友: 匿名 发表于: 2003-4-27 20:26:29 <BR><BR>我以前大学时学过C语言，但工作后就一直没用了(大概有两年了)，现在想从头学，请问我可以直接学习vc++吗？如果可以，请问我有什么地方要注意的吗？ <BR><BR>我想请您告诉我您的邮箱地址和您的QQ号码，这样我有问题就可以向您请教了，好吗？ <BR>如同意，可发到我的邮箱：hw.wh.cn@163.net <BR>谢谢！ <BR><BR>网友: qlong(zhanglongping@163.com) 发表于: 2003-4-25 19:59:09 <BR><BR>刚接触VC编程的朋友往往对许多数据类型的转换感到迷惑不解，本文将介绍一些常用数据类型的使用。 <BR><BR>我们先定义一些常见类型变量借以说明 <BR><BR>int i = 100; <BR>long l = 2001; <BR>float f=300.2; <BR>double d=12345.119; <BR>char username[]="女侠程佩君"; <BR>char temp[200]; <BR>char *buf; <BR>CString str; <BR>_variant_t v1; <BR>_bstr_t v2; <BR><BR>一、其它数据类型转换为字符串 <BR><BR>短整型(int) <BR>itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制 <BR>itoa(i,temp,2); ///按二进制方式转换 <BR>长整型(long) <BR>ltoa(l,temp,10); <BR><BR><BR>二、从其它包含字符串的变量中获取指向该字符串的指针 <BR><BR>CString变量 <BR>str = "2008北京奥运"; <BR>buf = (LPSTR)(LPCTSTR)str; <BR>BSTR类型的_variant_t变量 <BR>v1 = (_bstr_t)"程序员"; <BR>buf = _com_util::ConvertBSTRToString((_bstr_t)v1); <BR><BR>三、字符串转换为其它数据类型 <BR>strcpy(temp,"123"); <BR><BR>短整型(int) <BR>i = atoi(temp); <BR>长整型(long) <BR>l = atol(temp); <BR>浮点(double) <BR>d = atof(temp); <BR><BR>四、其它数据类型转换到CString <BR>使用CString的成员函数Format来转换,例如: <BR><BR>整数(int) <BR>str.Format("%d",i); <BR>浮点数(float) <BR>str.Format("%f",i); <BR>字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值 <BR>str = username; <BR><BR>五、BSTR、_bstr_t与CComBSTR <BR><BR>CComBSTR、_bstr_t是对BSTR的封装,BSTR是指向字符串的32位指针。 <BR>char *转换到BSTR可以这样: BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上头文件comutil.h <BR>反之可以使用char *p=_com_util::ConvertBSTRToString(b); <BR><BR><BR>六、VARIANT 、_variant_t 与 COleVariant <BR><BR>VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。 <BR>对于VARIANT变量的赋值：首先给vt成员赋值，指明数据类型，再对联合结构中相同数据类型的变量赋值，举个例子： <BR>VARIANT va; <BR>int a=2001; <BR>va.vt=VT_I4;///指明整型数据 <BR>va.lVal=a; ///赋值 <BR><BR>对于不马上赋值的VARIANT，最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系: <BR><BR>unsigned char bVal; VT_UI1 <BR>short iVal; VT_I2 <BR>long lVal; VT_I4 <BR>float fltVal; VT_R4 <BR>double dblVal; VT_R8 <BR>VARIANT_BOOL boolVal; VT_BOOL <BR>SCODE scode; VT_ERROR <BR>CY cyVal; VT_CY <BR>DATE date; VT_DATE <BR>BSTR bstrVal; VT_BSTR <BR>IUnknown FAR* punkVal; VT_UNKNOWN <BR>IDispatch FAR* pdispVal; VT_DISPATCH <BR>SAFEARRAY FAR* parray; VT_ARRAY|* <BR>unsigned char FAR* pbVal; VT_BYREF|VT_UI1 <BR>short FAR* piVal; VT_BYREF|VT_I2 <BR>long FAR* plVal; VT_BYREF|VT_I4 <BR>float FAR* pfltVal; VT_BYREF|VT_R4 <BR>double FAR* pdblVal; VT_BYREF|VT_R8 <BR>VARIANT_BOOL FAR* pboolVal; VT_BYREF|VT_BOOL <BR>SCODE FAR* pscode; VT_BYREF|VT_ERROR <BR>CY FAR* pcyVal; VT_BYREF|VT_CY <BR>DATE FAR* pdate; VT_BYREF|VT_DATE <BR>BSTR FAR* pbstrVal; VT_BYREF|VT_BSTR <BR>IUnknown FAR* FAR* ppunkVal; VT_BYREF|VT_UNKNOWN <BR>IDispatch FAR* FAR* ppdispVal; VT_BYREF|VT_DISPATCH <BR>SAFEARRAY FAR* FAR* pparray; VT_ARRAY|* <BR>VARIANT FAR* pvarVal; VT_BYREF|VT_VARIANT <BR>void FAR* byref; VT_BYREF <BR><BR>_variant_t是VARIANT的封装类，其赋值可以使用强制类型转换，其构造函数会自动处理这些数据类型。 <BR>例如： <BR>long l=222; <BR>ing i=100; <BR>_variant_t lVal(l); <BR>lVal = (long)i; <BR><BR>COleVariant的使用与_variant_t的方法基本一样，请参考如下例子： <BR>COleVariant v3 = "字符串", v4 = (long)1999; <BR>CString str =(BSTR)v3.pbstrVal; <BR>long i = v4.lVal; <BR><BR>七、其它 <BR><BR>对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如： <BR>LPARAM lParam; <BR>WORD loValue = LOWORD(lParam);///取低16位 <BR>WORD hiValue = HIWORD(lParam);///取高16位 <BR>对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如: <BR>WORD wValue; <BR>BYTE loValue = LOBYTE(wValue);///取低8位 <BR>BYTE hiValue = HIBYTE(wValue);///取高8位 <BR></TD></TR></TBODY></TABLE><img src ="http://www.cppblog.com/ivenher/aggbug/2205.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-12-28 11:33 <a href="http://www.cppblog.com/ivenher/articles/2205.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于CString总结</title><link>http://www.cppblog.com/ivenher/articles/1056.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 11 Nov 2005 03:16:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/1056.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/1056.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/1056.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/1056.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/1056.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 16pt; FONT-FAMILY: 隶书; mso-bidi-font-size: 12.0pt"><SPAN lang=EN-US><o:p>&nbsp;
<P align=center><SPAN>关于<SPAN lang=EN-US>CString总结</SPAN></P>
<P><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P><SPAN lang=EN-US>&nbsp;</SPAN><SPAN>前言：串操作是编程中最常用也最基本的操作之一。</SPAN> <SPAN>做为</SPAN><SPAN lang=EN-US>VC</SPAN><SPAN>程序员，无论是菜鸟或高手都曾用过</SPAN><SPAN lang=EN-US>Cstring</SPAN><SPAN>。而且好像实际编程中很难离得开它（虽然它不是标准Ｃ</SPAN><SPAN lang=EN-US>++</SPAN><SPAN>中的库）。因为</SPAN><SPAN lang=EN-US>MFC</SPAN><SPAN>中提供的这个类对我们操作字串实在太方便了，</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>不仅提供各种丰富的操作函数、操作符重载，使我们使用起串起来更象</SPAN><SPAN lang=EN-US>basic</SPAN><SPAN>中那样直观；而且它还提供了动态内存分配，使我们减少了多少字符串数组越界的隐患。但是，我们在使用过程中也体会到</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>简直太容易出错了，而且有的不可捉摸。所以有许多高人站过来，建议抛弃它。</SPAN></P>
<P><SPAN>在此，我个人认为：</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>封装得确实很完美，它有许多优点，如“容易使用</SPAN> <SPAN>，功能强，动态分配内存，大量进行拷贝时它很能节省内存资源并且执行效率高，与标准Ｃ完全兼容，同时支持多字节与宽字节，由于有异常机制所以使用它安全方便”</SPAN> <SPAN>其实，使用过程中之所以容易出错，那是因为我们对它了解得还不够，特别是它的实现机制。因为我们中的大多数人，在工作中并不那么爱深入地去看关于它的文档，何况它还是英文的。</SPAN><SPAN lang=EN-US> </SPAN></P>
<P><SPAN>由于前几天我在工作中遇到了一个本不是问题但却特别棘手、特别难解决而且莫名惊诧的问题。好来最后发现是由于</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>引发的。所以没办法，我把整个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>的实现全部看了一遍，才慌然大悟，并彻底弄清了问题的原因</SPAN><SPAN lang=EN-US>(</SPAN><SPAN>这个问题，我已在</SPAN><SPAN lang=EN-US>csdn</SPAN><SPAN>上开贴</SPAN><SPAN lang=EN-US>)</SPAN><SPAN>。在此，我想把我的一些关于</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>的知识总结一番，以供他（她）人借鉴，也许其中有我理解上的错误，望发现者能通知我，不胜感谢。</SPAN></P>
<P><SPAN lang=EN-US></SPAN></P>
<P><SPAN lang=EN-US>1</SPAN><SPAN>．</SPAN><SPAN lang=EN-US> CString</SPAN><SPAN>实现的机制</SPAN><SPAN lang=EN-US>.</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>CString</SPAN><SPAN>是通过“引用”来管理串的，“引用”这个词我相信大家并不陌生，象</SPAN><SPAN lang=EN-US>Window</SPAN><SPAN>内核对象、</SPAN><SPAN lang=EN-US>COM</SPAN><SPAN>对象等都是通过引用来实现的。而</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>也是通过这样的机制来管理分配的内存块。实际上</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>对象只有一个指针成员变量</SPAN><SPAN lang=EN-US>,</SPAN><SPAN>所以任何</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>实例的长度只有</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>字节</SPAN><SPAN lang=EN-US>.</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>即</SPAN><SPAN lang=EN-US>: int len = sizeof(CString);//len</SPAN><SPAN>等于</SPAN><SPAN lang=EN-US>4</SPAN></P>
<P><SPAN>这个指针指向一个相关的引用内存块，如图</SPAN><SPAN lang=EN-US>: CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US><SPAN lang=EN-US><SPAN lang=EN-US><SPAN lang=EN-US>0x04040404 <SPAN>&nbsp;</SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN>&nbsp;</SPAN>head</SPAN><SPAN>部，为引用内存块相关信息</SPAN></P>
<P><SPAN lang=EN-US><SPAN lang=EN-US>str<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>0x40404040</SPAN></P>
<P><SPAN lang=EN-US><SPAN lang=EN-US><SPAN lang=EN-US><SPAN lang=EN-US>
<P><SPAN lang=EN-US></SPAN></P><SPAN>正因为如此，一个这样的内存块可被多个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>所引用，例如下列代码：</SPAN> 
<P></P>
<P><SPAN lang=EN-US>CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US>CString a = str;</SPAN></P>
<P><SPAN lang=EN-US>CString b(str);</SPAN></P>
<P><SPAN lang=EN-US>CString c;</SPAN></P>
<P><SPAN lang=EN-US>c = b;</SPAN></P>
<P><SPAN>上面代码的结果是：上面四个对象</SPAN><SPAN lang=EN-US>(str,a,b,c)</SPAN><SPAN>中的成员变量指针有相同的值，都为</SPAN><SPAN lang=EN-US>0x40404040.</SPAN><SPAN>而这块内存块怎么知道有多少个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>引用它呢？同样，它也会记录一些信息。如被引用数，串长度，分配内存长度。</SPAN></P>
<P><SPAN>这块引用内存块的结构定义如下：</SPAN></P>
<P><SPAN lang=EN-US>struct CStringData</SPAN></P>
<P><SPAN lang=EN-US>{</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>long nRefs;<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>//</SPAN><SPAN>表示有多少个</SPAN><SPAN lang=EN-US>CString </SPAN><SPAN>引用它</SPAN><SPAN lang=EN-US>. 4</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>int nDataLength;<SPAN>&nbsp; </SPAN>//</SPAN><SPAN>串实际长度</SPAN><SPAN lang=EN-US>. 4</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>int nAllocLength; //</SPAN><SPAN>总共分配的内存长度（不计这头部的</SPAN><SPAN lang=EN-US>12</SPAN><SPAN>字节）</SPAN><SPAN lang=EN-US>. 4</SPAN></P>
<P><SPAN lang=EN-US>};</SPAN></P>
<P><SPAN>由于有了这些信息，</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>就能正确地分配、管理、释放引用内存块。</SPAN></P>
<P><SPAN>如果你想在调试程序的时候获得这些信息。可以在</SPAN><SPAN lang=EN-US>Watch</SPAN><SPAN>窗口键入下列表达式：</SPAN></P>
<P><SPAN lang=EN-US>(CStringData*)((CStringData*)(this-&gt;m_pchData)-1)</SPAN><SPAN>或</SPAN></P>
<P><SPAN lang=EN-US>(CStringData*)((CStringData*)(str.m_pchData)-1)//str</SPAN><SPAN>为指</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>实例</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN>正因为采用了这样的好机制，使得</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>在大量拷贝时，不仅效率高，而且分配内存少。</SPAN></P>
<P>
<P><SPAN lang=EN-US></SPAN></P><SPAN lang=EN-US>2</SPAN><SPAN>．</SPAN><SPAN lang=EN-US>LPCTSTR </SPAN><SPAN>与</SPAN><SPAN lang=EN-US> GetBuffer(int nMinBufLength) </SPAN>
<P></P>
<P><SPAN>这两个函数提供了与标准</SPAN><SPAN lang=EN-US>C</SPAN><SPAN>的兼容转换。在实际中使用频率很高，但却是最容易出错的地方。这两个函数实际上返回的都是指针，但它们有何区别呢？以及调用它们后，幕后是做了怎样的处理过程呢？</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>(1) LPCTSTR </SPAN><SPAN>它的执行过程其实很简单，只是返回引用内存块的串地址。</SPAN> <SPAN>它是作为操作符重载提供的，所以在代码中有时可以隐式转换，而有时却需强制转制。如：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>const char* p = (LPCTSTR)str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>//</SPAN><SPAN>假设有这样的一个函数，</SPAN><SPAN lang=EN-US>Test(const char* p)</SPAN><SPAN>；</SPAN><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>你就可以这样调用</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Test(str);//</SPAN><SPAN>这里会隐式转换为</SPAN><SPAN lang=EN-US>LPCTSTR</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>(2) GetBuffer(int nMinBufLength) </SPAN><SPAN>它类似，也会返回一个指针，不过它有点差别</SPAN><SPAN lang=EN-US>,</SPAN><SPAN>返回的是</SPAN><SPAN lang=EN-US>LPTSTR</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>(3) </SPAN><SPAN>这两者到底有何不同呢？我想告诉大家，其本质上完全不一样，一般说</SPAN><SPAN lang=EN-US>LPCTSTR</SPAN><SPAN>转换后只应该当常量使用，或者做函数的入参；而</SPAN><SPAN lang=EN-US>GetBuffer(...)</SPAN><SPAN>取出指针后，可以通过这个指针来修改里面的内容，或者做函数的出参。为什么呢？也许经常有这样的代码：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char* p = (char*)(const char*)str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>p[2] = 'z';<SPAN>&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>其实，也许有这样的代码后，你的程序并没有错，而且程序也运行得挺好。但它却是非常危险的。再看</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString test = str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>....</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char* p = (char*)(const char*)str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>p[2] = 'z';<SPAN>&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>strcpy(p, "akfjaksjfakfakfakj");//</SPAN><SPAN>这下完蛋了</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>你知道此时，</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>中的值是多少吗？答案是</SPAN><SPAN lang=EN-US>"abzd"</SPAN><SPAN>。它也跟着改变了，这不是你所期望发生的。但为什么会这样呢？你稍微想想就会明白，前面说过，因为</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>是指向引用块的，</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>指向同一块地方</SPAN><SPAN lang=EN-US>,</SPAN><SPAN>当你</SPAN><SPAN lang=EN-US>p[2]='z'</SPAN><SPAN>后，当然</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>也会随着改变。所以用它做</SPAN><SPAN lang=EN-US>LPCTSTR</SPAN><SPAN>做转换后，你只能去读这块数据，千万别去改变它的内容。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></P>
<P><SPAN>假如我想直接通过指针去修改数据的话，那怎样办呢？就是用</SPAN><SPAN lang=EN-US>GetBuffer(...).</SPAN><SPAN>看下述代码：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString test = str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>....</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char* p = str.GetBuffer(20);</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>p[2] = 'z';<SPAN>&nbsp; </SPAN>//<SPAN>&nbsp;&nbsp; </SPAN><SPAN>执行到此，现在</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>中值却仍是</SPAN><SPAN lang=EN-US>"abcd"</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>strcpy(p, "akfjaksjfakfakfakj");<SPAN>&nbsp;&nbsp; </SPAN>//<SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>执行到此，现在</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>中值还是</SPAN><SPAN lang=EN-US>"abcd"</SPAN></P>
<P><SPAN>为什么会这样？其实</SPAN><SPAN lang=EN-US>GetBuffer(20)</SPAN><SPAN>调用时，它实际上另外建立了一块新内块存，并分配</SPAN><SPAN lang=EN-US>20</SPAN><SPAN>字节长度的</SPAN><SPAN lang=EN-US>buffer</SPAN><SPAN>，而原来的内存块引用计数也相应减</SPAN><SPAN lang=EN-US>1.<SPAN>&nbsp; </SPAN><SPAN>所以执行代码后</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>test</SPAN><SPAN>是指向了两块不同的地方，所以相安无事。</SPAN></P>
<P><SPAN lang=EN-US>(4) </SPAN><SPAN>不过这里还有一点注意事项：就是</SPAN><SPAN lang=EN-US>str.GetBuffer(20)</SPAN><SPAN>后，</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>的分配长度为</SPAN><SPAN lang=EN-US>20</SPAN><SPAN>，即指针</SPAN><SPAN lang=EN-US>p</SPAN><SPAN>它所指向的</SPAN><SPAN lang=EN-US>buffer</SPAN><SPAN>只有</SPAN><SPAN lang=EN-US>20</SPAN><SPAN>字节长，给它赋值时，切不可超过，否则灾难离你不远了；如果指定长度小于原来串长度，如</SPAN><SPAN lang=EN-US>GetBuffer(1),</SPAN><SPAN>实际上它会分配</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>个字节长度（即原来串长度）；另外，当调用</SPAN><SPAN lang=EN-US>GetBuffer(...)</SPAN><SPAN>后并改变其内容，一定要记得调用</SPAN><SPAN lang=EN-US>ReleaseBuffer(),</SPAN><SPAN>这个函数会根据串内容来更新引用内存块的头部信息。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(5) </SPAN><SPAN>最后还有一注意事项，看下述代码：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char* p = NULL;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>const char* q = NULL;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str = "abcd";</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>q = (LPCTSTR)str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>p = str.GetBuffer(20);</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>AfxMessageBox(q);// </SPAN><SPAN>合法的</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>strcpy(p, "this is test");//</SPAN><SPAN>合法的，</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>AfxMessageBox(q);// </SPAN><SPAN>非法的，可能完蛋</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>strcpy(p, "this is test");//</SPAN><SPAN>非法的，可能完蛋</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>这里要说的就是，当返回这些指针后，</SPAN> <SPAN>如果</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>对象生命结束，这些指针也相应无效。</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>3</SPAN><SPAN>．拷贝</SPAN><SPAN lang=EN-US> &amp; </SPAN><SPAN>赋值</SPAN><SPAN lang=EN-US> &amp; "</SPAN><SPAN>引用内存块</SPAN><SPAN lang=EN-US>" </SPAN><SPAN>什么时候释放？</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>下面演示一段代码执行过程</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>void Test()</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>{</SPAN></P>
<P><SPAN lang=EN-US>CString str("abcd");</SPAN></P>
<P><SPAN lang=EN-US>//str</SPAN><SPAN>指向一引用内存块（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US>CString a;</SPAN></P>
<P><SPAN lang=EN-US>//a</SPAN><SPAN>指向一初始数据状态，</SPAN></P>
<P><SPAN lang=EN-US>a = str;</SPAN></P>
<P><SPAN lang=EN-US>//a</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>指向同一引用内存块（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>2,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US>CString b(a);</SPAN></P>
<P><SPAN lang=EN-US>//a</SPAN><SPAN>、</SPAN><SPAN lang=EN-US>b</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>指向同一引用内存块（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>3,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>LPCTSTR temp = (LPCTSTR)a;</SPAN></P>
<P><SPAN lang=EN-US>//temp</SPAN><SPAN>指向引用内存块的串首地址。（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>3,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString d = a;</SPAN></P>
<P><SPAN lang=EN-US>//a</SPAN><SPAN>、</SPAN><SPAN lang=EN-US>b</SPAN><SPAN>、</SPAN><SPAN lang=EN-US>d</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>指向同一引用内存块（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>4,<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>b = "testa";</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>这条语句实际是调用</SPAN><SPAN lang=EN-US>CString::operator=(CString&amp;)</SPAN><SPAN>函数。</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>b</SPAN><SPAN>指向一新分配的引用内存块。（新分配的引用内存块的</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>引用计数为</SPAN><SPAN lang=EN-US>1, </SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>5, </SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>5</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>同时原引用内存块引用计数减</SPAN><SPAN lang=EN-US>1. a</SPAN><SPAN>、</SPAN><SPAN lang=EN-US>d</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>仍指向原</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>引用内存块（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>3,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>由于</SPAN><SPAN lang=EN-US>d</SPAN><SPAN>生命结束，调用析构函数，导至引用计数减</SPAN><SPAN lang=EN-US>1</SPAN><SPAN>（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>2,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US>LPTSTR temp = a.GetBuffer(10);</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>此语句也会导致重新分配新内存块。</SPAN><SPAN lang=EN-US>temp</SPAN><SPAN>指向新分配引用内存块的串首地址（新</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>分配的引用内存块的引用计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>0,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>10</SPAN><SPAN>）</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>同时原引用内存块引用计数减</SPAN><SPAN lang=EN-US>1. <SPAN>&nbsp;</SPAN><SPAN>只有</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>仍</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>指向原引用内存块</SPAN> <SPAN>（引用内存块的引用计数为</SPAN><SPAN lang=EN-US>1,<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4, </SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>4</SPAN><SPAN>）</SPAN><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US>strcpy(temp, "temp"); </SPAN></P>
<P><SPAN lang=EN-US>//a</SPAN><SPAN>指向的引用内存块的引用计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>0,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>10 a.ReleaseBuffer();//</SPAN><SPAN>注意</SPAN><SPAN lang=EN-US>:a</SPAN><SPAN>指向的引用内存块的引用计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>4,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>10</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>}</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>//</SPAN><SPAN>执行到此，所有的局部变量生命周期都已结束。对象</SPAN><SPAN lang=EN-US>str a b </SPAN><SPAN>各自调用自己的析构构</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>//</SPAN><SPAN>函数，所指向的引用内存块也相应减</SPAN><SPAN lang=EN-US>1</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>//</SPAN><SPAN>注意，</SPAN><SPAN lang=EN-US>str a b </SPAN><SPAN>所分别指向的引用内存块的计数均为</SPAN><SPAN lang=EN-US>0,</SPAN><SPAN>这导致所分配的内存块释放</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>通过观察上面执行过程，我们会发现</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>虽然可以多个对象指向同一引用内块存，但是它们在进行各种拷贝、赋值及改变串内容时，它的处理是很智能并且非常安全的，完全做到了互不干涉、互不影响。当然必须要求你的代码使用正确恰当，特别是实际使用中会有更复杂的情况，如做函数参数、引用、及有时需保存到</SPAN><SPAN lang=EN-US>CStringList</SPAN><SPAN>当中，如果哪怕有一小块地方使用不当，其结果也会导致发生不可预知的错误</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>5<SPAN>&nbsp; </SPAN>FreeExtra()</SPAN><SPAN>的作用</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>看这段代码</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(1)<SPAN>&nbsp;&nbsp; </SPAN>CString str("test");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(2)<SPAN>&nbsp;&nbsp; </SPAN>LPTSTR temp = str.GetBuffer(50);</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(3)<SPAN>&nbsp;&nbsp; </SPAN>strcpy(temp, "there are 22 character");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(4)<SPAN>&nbsp;&nbsp; </SPAN>str.ReleaseBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(5)<SPAN>&nbsp;&nbsp; </SPAN>str.FreeExtra();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>上面代码执行到第</SPAN><SPAN lang=EN-US>(4)</SPAN><SPAN>行时，大家都知道</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>指向的引用内存块计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>22,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>50. </SPAN><SPAN>那么执行</SPAN><SPAN lang=EN-US>str.FreeExtra()</SPAN><SPAN>时，它会释放所分配的多余的内存。</SPAN><SPAN lang=EN-US>(</SPAN><SPAN>引用内存块计数为</SPAN><SPAN lang=EN-US>1,</SPAN><SPAN>长度为</SPAN><SPAN lang=EN-US>22,</SPAN><SPAN>分配长度为</SPAN><SPAN lang=EN-US>22)</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>6<SPAN>&nbsp; </SPAN>Format(...)<SPAN>&nbsp; </SPAN><SPAN>与</SPAN><SPAN lang=EN-US> FormatV(...)</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>这条语句在使用中是最容易出错的。因为它最富有技巧性，也相当灵活。在这里，我没打算对它细细分析，实际上</SPAN><SPAN lang=EN-US>sprintf(...)</SPAN><SPAN>怎么用，它就怎么用。我只提醒使用时需注意一点：就是它的参数的特殊性，由于编译器在编译时并不能去校验格式串参数与对应的变元的类型及长度。所以你必须要注意，两者一定要对应上，</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>否则就会出错。如：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>CString str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>int a = 12;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>str.Format("first:%l, second: %s", a, "error");//result?</SPAN><SPAN>试试</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>7<SPAN>&nbsp; </SPAN>LockBuffer() </SPAN><SPAN>与</SPAN><SPAN lang=EN-US> UnlockBuffer()</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>顾名思议，这两个函数的作用就是对引用内存块进行加锁及解锁。但使用它有什么作用及执行过它后对</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>串有什么实质上的影响。其实挺简单，看下面代码</SPAN><SPAN lang=EN-US>:</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(1)<SPAN>&nbsp;&nbsp; </SPAN>CString str("test");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(2)<SPAN>&nbsp;&nbsp; </SPAN>str.LockBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(3)<SPAN>&nbsp;&nbsp; </SPAN>CString temp = str;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(4)<SPAN>&nbsp;&nbsp; </SPAN>str.UnlockBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(5)<SPAN>&nbsp;&nbsp; </SPAN>str.LockBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(6)<SPAN>&nbsp;&nbsp; </SPAN>str = "error";</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(7)<SPAN>&nbsp;&nbsp; </SPAN>str.ReleaseBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>执行完</SPAN><SPAN lang=EN-US>(3)</SPAN><SPAN>后，与通常情况下不同，</SPAN><SPAN lang=EN-US>temp</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>并不指向同一引用内存块。你可以在</SPAN><SPAN lang=EN-US>watch</SPAN><SPAN>窗口用这个表达式</SPAN><SPAN lang=EN-US>(CStringData*)((CStringData*)(str.m_pchData)-1)</SPAN><SPAN>看看。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>其实在</SPAN><SPAN lang=EN-US>msdn</SPAN><SPAN>中有说明：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>While in a locked state, the string is protected in two ways: </SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>No other string can get a reference to the data in the locked string, even if that string is assigned to the locked string.</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>The locked string will never reference another string, even if that other string is copied to the locked string. </SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>8<SPAN>&nbsp; </SPAN>CString </SPAN><SPAN>只是处理串吗？</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>不对，</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>不只是能操作串，而且还能处理内存块数据。功能完善吧！看这段代码</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char<SPAN>&nbsp; </SPAN>p[20];</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>for(int loop=0; loop&lt;sizeof(p); loop++)</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>p[loop] = 10-loop;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>}</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>CString str((LPCTSTR)p, 20);</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>char temp[20];</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>memcpy(temp, str, str.GetLength());</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>str</SPAN><SPAN>完全能够转载内存块</SPAN><SPAN lang=EN-US>p</SPAN><SPAN>到内存块</SPAN><SPAN lang=EN-US>temp</SPAN><SPAN>中。所以能用</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>来处理二进制数据</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>8<SPAN>&nbsp; </SPAN>AllocSysString()</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>SetSysString(BSTR*) </SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>这两个函数提供了串与</SPAN><SPAN lang=EN-US>BSTR</SPAN><SPAN>的转换。使用时须注意一点：当调用</SPAN><SPAN lang=EN-US>AllocSysString()</SPAN><SPAN>后，须调用它</SPAN><SPAN lang=EN-US>SysFreeString(...) </SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>9<SPAN>&nbsp; </SPAN><SPAN>参数的安全检验</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>在</SPAN><SPAN lang=EN-US>MFC</SPAN><SPAN>中提供了多个宏来进行参数的安全检查，如：</SPAN><SPAN lang=EN-US>ASSERT.<SPAN>&nbsp; </SPAN><SPAN>其中在</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>中也不例外，有许多这样的参数检验，其实这也说明了代码的安全性高，可有时我们会发现这很烦，也导致</SPAN><SPAN lang=EN-US>Debug</SPAN><SPAN>与</SPAN><SPAN lang=EN-US>Release</SPAN><SPAN>版本不一样，如有时程序</SPAN><SPAN lang=EN-US>Debug</SPAN><SPAN>通正常，而</SPAN><SPAN lang=EN-US>Release</SPAN><SPAN>则程序崩溃；而有时恰相反，</SPAN><SPAN lang=EN-US>Debug</SPAN><SPAN>不行，</SPAN><SPAN lang=EN-US>Release</SPAN><SPAN>行。其实我个人认为，我们对</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>的使用过程中，应力求代码质量高，不能在</SPAN><SPAN lang=EN-US>Debug</SPAN><SPAN>版本中出现任何断言框，哪怕</SPAN><SPAN lang=EN-US>release</SPAN><SPAN>运行似乎看起来一切正常。但很不安全。如下代码：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(1)<SPAN>&nbsp;&nbsp; </SPAN>CString str("test");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(2)<SPAN>&nbsp;&nbsp; </SPAN>str.LockBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(3)<SPAN>&nbsp;&nbsp; </SPAN>LPTSTR temp = str.GetBuffer(10);</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(4)<SPAN>&nbsp;&nbsp; </SPAN>strcpy(temp, "error");</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(5)<SPAN>&nbsp;&nbsp; </SPAN>str.ReleaseBuffer();</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>(6)<SPAN>&nbsp;&nbsp; </SPAN>str.ReleaseBuffer();//</SPAN><SPAN>执行到此时，</SPAN><SPAN lang=EN-US>Debug</SPAN><SPAN>版本会弹出错框</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>10 CString</SPAN><SPAN>的异常处理</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>我只想强调一点：只有分配内存时，才有可能导致抛出</SPAN><SPAN lang=EN-US>CMemoryException.</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN><SPAN>同样，在</SPAN><SPAN lang=EN-US>msdn</SPAN><SPAN>中的函数声明中，注有</SPAN><SPAN lang=EN-US>throw( CMemoryException)</SPAN><SPAN>的函数都有重新分配或调整内存的可能。</SPAN></P>
<P><SPAN lang=EN-US><SPAN></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US>11 </SPAN><SPAN>跨模块时的</SPAN><SPAN lang=EN-US>Cstring</SPAN><SPAN>。即一个</SPAN><SPAN lang=EN-US>DLL</SPAN><SPAN>的接口函数中的参数为</SPAN><SPAN lang=EN-US>CString&amp;</SPAN><SPAN>时，它会发生怎样的现象。解答我遇到的问题。我的问题原来已经发贴，地址为：</SPAN></P>
<P><SPAN lang=EN-US>http://www.csdn.net/expert/topic/741/741921.xml?temp=.2283136</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>构造一个这样</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>对象时，如</SPAN><SPAN lang=EN-US>CString str</SPAN><SPAN>，你可知道此时的</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>所指向的引用内存块吗？也许你会认为它指向</SPAN><SPAN lang=EN-US>NULL</SPAN><SPAN>。其实不对，如果这样的话，</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>所采用的引用机制管理内存块就会有麻烦了，所以</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>在构造一个空串的对象时，它会指向一个固定的初始化地址，这块数据的声明如下：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp; </SPAN>AFX_STATIC_DATA int _afxInitData[] = {-1,0,0,0};</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>简要描述概括一下：当某个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>对象串置空的话，如</SPAN><SPAN lang=EN-US>Empty(),CString a</SPAN><SPAN>等，它的成员变量</SPAN><SPAN lang=EN-US>m_pchData</SPAN><SPAN>就会指向</SPAN><SPAN lang=EN-US>_afxInitData</SPAN><SPAN>这个变量的地址。当这个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>对象生命周期结束时，正常情况下它会去对所指向的引用内存块计数减</SPAN><SPAN lang=EN-US>1</SPAN><SPAN>，如果引用计数为</SPAN><SPAN lang=EN-US>0(</SPAN><SPAN>即没有任何</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>引用它时</SPAN><SPAN lang=EN-US>)</SPAN><SPAN>，则释放这块引用内存。而现在的情况是如果</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>所指向的引用内存块是初始化内存块时，则不会释放任何内存。</SPAN></P>
<P><SPAN lang=EN-US></SPAN>&nbsp;</P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>说了这么多，这与我遇到的问题有什么关系呢？其实关系大着呢？其真正原因就是如果</SPAN><SPAN lang=EN-US>exe</SPAN><SPAN>模块与</SPAN><SPAN lang=EN-US>dll</SPAN><SPAN>模块有一个是</SPAN><SPAN lang=EN-US>static</SPAN><SPAN>编译连接的话。那么这个</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>初始化数据在</SPAN><SPAN lang=EN-US>exe</SPAN><SPAN>模块与</SPAN><SPAN lang=EN-US>dll</SPAN><SPAN>模块中有不同的地址，因为</SPAN><SPAN lang=EN-US>static</SPAN><SPAN>连接则会在本模块中有一份源代码的拷贝。另外一种情况，如果两个模块都是</SPAN><SPAN lang=EN-US>share</SPAN><SPAN>连接的，</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>的实现代码则在另一个单独的</SPAN><SPAN lang=EN-US>dll</SPAN><SPAN>中实现，而</SPAN><SPAN lang=EN-US>AFX_STATIC_DATA</SPAN><SPAN>指定变量只装一次，所以两个模块中</SPAN><SPAN lang=EN-US>_afxInitData</SPAN><SPAN>有相同的地址。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN><SPAN>现在问题完全明白了吧！你可以自己去演示一下。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>__declspec (dllexport) void test(CString&amp; str)</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>{</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN>str = "abdefakdfj";//</SPAN><SPAN>如果是</SPAN><SPAN lang=EN-US>static</SPAN><SPAN>连接，并且传入的</SPAN><SPAN lang=EN-US>str</SPAN><SPAN>为空串的话，这里出错。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp; </SPAN>}</SPAN></P>
<P><SPAN lang=EN-US><SPAN></SPAN>&nbsp;</P>
<P><SPAN>最后一点想法：写得这里，其实</SPAN><SPAN lang=EN-US>CString</SPAN><SPAN>中还有许多技巧性的好东东，我并没去解释。如很多重载的操作符、查找等。我认为还是详细看看</SPAN><SPAN lang=EN-US>msdn</SPAN><SPAN>，这样也许会比我讲的好多了。我只侧重那些可能会出错的情况。当然，如我上面叙述中有错误，敬请高手指点，不胜感谢！</SPAN></P></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></o:p></SPAN></SPAN><img src ="http://www.cppblog.com/ivenher/aggbug/1056.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-11-11 11:16 <a href="http://www.cppblog.com/ivenher/articles/1056.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针函数和函数指针</title><link>http://www.cppblog.com/ivenher/articles/915.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 03 Nov 2005 03:26:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/915.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/915.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/915.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/915.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">一、指针函数<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>当一个函数声明其返回值为一个指针时，实际上就是返回一个地址给调用函数，以用于需要指针或地址的表达式中。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>格式：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>类型说明符<SPAN lang=EN-US>&nbsp;*&nbsp;</SPAN>函数名<SPAN lang=EN-US>(</SPAN>参数<SPAN lang=EN-US>)<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>当然了，由于返回的是一个地址，所以类型说明符一般都是<SPAN lang=EN-US>int</SPAN>。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例如：<SPAN lang=EN-US>int&nbsp;*GetDate();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*&nbsp;aaa(int,int);<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>函数返回的是一个地址值，经常使用在返回数组的某一元素地址上。<SPAN lang=EN-US><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*&nbsp;GetDate(int&nbsp;wk,int&nbsp;dy);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;wk,dy;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Enter&nbsp;week(1-5)day(1-7)\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scanf("%d%d",&amp;wk,&amp;dy);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(wk&lt;1||wk&gt;5||dy&lt;1||dy&gt;7);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\n",*GetDate(wk,dy));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*&nbsp;GetDate(int&nbsp;wk,int&nbsp;dy)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;int&nbsp;calendar[5][7]=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{1,2,3,4,5,6,7},<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{8,9,10,11,12,13,14},<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{15,16,17,18,19,20,21},<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{22,23,24,25,26,27,28},<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{29,30,31,-1}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&amp;calendar[wk-1][dy-1];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>程序应该是很好理解的，子函数返回的是数组某元素的地址。输出的是这个地址里的值。<SPAN lang=EN-US><BR><BR></SPAN>二、函数指针<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指向函数的指针包含了函数的地址，可以通过它来调用函数。声明格式如下：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>类型说明符<SPAN lang=EN-US>&nbsp;(*</SPAN>函数名<SPAN lang=EN-US>)(</SPAN>参数<SPAN lang=EN-US>)<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>其实这里不能称为函数名，应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明笔削和它指向函数的声明保持一致。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号，就变成了一个返回整型指针的函数的原型声明。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例如：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;(*fptr)();<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>把函数的地址赋值给函数指针，可以采用下面两种形式：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fptr=&amp;Function;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fptr=Function;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>取地址运算符<SPAN lang=EN-US>&amp;</SPAN>不是必需的，因为单单一个函数标识符就标号表示了它的地址，如果是函数调用，还必须包含一个圆括号括起来的参数表。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>可以采用如下两种方式来通过指针调用函数：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=(*fptr)();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x=fptr();<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>第二种格式看上去和函数调用无异。但是有些程序员倾向于使用第一种格式，因为它明确指出是通过指针而非函数名来调用函数的。下面举一个例子：<SPAN lang=EN-US><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;(*funcp)();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;FileFunc(),EditFunc();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;funcp=FileFunc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*funcp)();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;funcp=EditFunc;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*funcp)();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;FileFunc()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("FileFunc\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;EditFunc()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("EditFunc\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>程序输出为：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileFunc<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EditFunc<BR><BR></SPAN>三、指针的指针<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针的指针看上去有些令人费解。它们的声明有两个星号。例如：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;**&nbsp;cp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>如果有三个星号，那就是指针的指针的指针，四个星号就是指针的指针的指针的指针，依次类推。当你熟悉了简单的例子以后，就可以应付复杂的情况了。当然，实际程序中，一般也只用到二级指针，三个星号不常见，更别说四个星号了。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针的指针需要用到指针的地址。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;c='A';<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*p=&amp;c;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;**cp=&amp;p;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>通过指针的指针，不仅可以访问它指向的指针，还可以访问它指向的指针所指向的数据。下面就是几个这样的例子：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*p1=*cp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;c1=**cp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>你可能想知道这样的结构有什么用。利用指针的指针可以允许被调用函数修改局部指针变量和处理指针数组。<SPAN lang=EN-US><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;FindCredit(int&nbsp;**);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;vals[]={7,6,5,-4,3,2,1,0};<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*fp=vals;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FindCredit(&amp;fp);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\n",*fp);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;FindCredit(int&nbsp;**&nbsp;fpp)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(**fpp!=0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(**fpp&lt;0)&nbsp;break;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;(*fpp)++;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>首先用一个数组的地址初始化指针<SPAN lang=EN-US>fp</SPAN>，然后把该指针的地址作为实参传递给函数<SPAN lang=EN-US>FindCredit()</SPAN>。<SPAN lang=EN-US>FindCredit()</SPAN>函数通过表达式<SPAN lang=EN-US>**fpp</SPAN>间接地得到数组中的数据。为遍历数组以找到一个负值，<SPAN lang=EN-US>FindCredit()</SPAN>函数进行自增运算的对象是调用者的指向数组的指针，而不是它自己的指向调用者指针的指针。语句<SPAN lang=EN-US>(*fpp)++</SPAN>就是对形参指针指向的指针进行自增运算的。但是因为<SPAN lang=EN-US>*</SPAN>运算符高于<SPAN lang=EN-US>++</SPAN>运算符，所以圆括号在这里是必须的，如果没有圆括号，那么<SPAN lang=EN-US>++</SPAN>运算符将作用于二重指针<SPAN lang=EN-US>fpp</SPAN>上。<SPAN lang=EN-US><BR><BR></SPAN>四、指向指针数组的指针<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针的指针另一用法旧处理指针数组。有些程序员喜欢用指针数组来代替多维数组，一个常见的用法就是处理字符串。<SPAN lang=EN-US><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*Names[]=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Bill",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Sam",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Jim",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Paul",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Charles",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;**nm=Names;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while(*nm!=0)&nbsp;printf("%s\n",*nm++);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>先用字符型指针数组<SPAN lang=EN-US>Names</SPAN>的地址来初始化指针<SPAN lang=EN-US>nm</SPAN>。每次<SPAN lang=EN-US>printf()</SPAN>的调用都首先传递指针<SPAN lang=EN-US>nm</SPAN>指向的字符型指针，然后对<SPAN lang=EN-US>nm</SPAN>进行自增运算使其指向数组的下一个元素<SPAN lang=EN-US>(</SPAN>还是指针<SPAN lang=EN-US>)</SPAN>。注意完成上述认为的语法为<SPAN lang=EN-US>*nm++</SPAN>，它首先取得指针指向的内容，然后使指针自增。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>注意数组中的最后一个元素被初始化为<SPAN lang=EN-US>0</SPAN>，<SPAN lang=EN-US>while</SPAN>循环以次来判断是否到了数组末尾。具有零值的指针常常被用做循环数组的终止符。程序员称零值指针为空指针<SPAN lang=EN-US>(NULL)</SPAN>。采用空指针作为终止符，在树种增删元素时，就不必改动遍历数组的代码，因为此时数组仍然以空指针作为结束。</SPAN><img src ="http://www.cppblog.com/ivenher/aggbug/915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-11-03 11:26 <a href="http://www.cppblog.com/ivenher/articles/915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数组和指针</title><link>http://www.cppblog.com/ivenher/articles/912.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Thu, 03 Nov 2005 02:06:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/912.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/912.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/912.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/912.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/912.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">指针和数组有着密切的关系<SPAN lang=EN-US>,</SPAN>任何能由数组下标完成的操作也都可用指针来实现<SPAN lang=EN-US>,</SPAN>但程序中使用指针可使代码更紧凑、更灵活。<SPAN lang=EN-US><BR><BR></SPAN>一、指向数组元素的指针<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>我们定义一个整型数组和一个指向整型的指针变量<SPAN lang=EN-US>:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a[10],&nbsp;*p;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>和前面介绍过的方法相同<SPAN lang=EN-US>,</SPAN>可以使整型指针<SPAN lang=EN-US>p</SPAN>指向数组中任何一个元素<SPAN lang=EN-US>,</SPAN>假定给出赋值运算<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p=&amp;a[0];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>此时<SPAN lang=EN-US>,p</SPAN>指向数组中的第<SPAN lang=EN-US>0</SPAN>号元素<SPAN lang=EN-US>,</SPAN>即<SPAN lang=EN-US>a[0],</SPAN>指针变量<SPAN lang=EN-US>p</SPAN>中包含了数组元素<SPAN lang=EN-US>a[0]</SPAN>的地址<SPAN lang=EN-US>,</SPAN>由于数组元素在内存中是连续存放的<SPAN lang=EN-US>,</SPAN>因此<SPAN lang=EN-US>,</SPAN>我们就可以通过指针变量<SPAN lang=EN-US>p</SPAN>及其有关运算间接访问数组中的任何一个元素。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;Turbo&nbsp;C</SPAN>中<SPAN lang=EN-US>,</SPAN>数组名是数组的第<SPAN lang=EN-US>0</SPAN>号元素的地址<SPAN lang=EN-US>,</SPAN>因此下面两个语句是等价的<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p=&amp;a[0];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p=a;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>根据地址运算规则<SPAN lang=EN-US>,a+1</SPAN>为<SPAN lang=EN-US>a[1]</SPAN>的地址<SPAN lang=EN-US>,a+i</SPAN>就为<SPAN lang=EN-US>a[i]</SPAN>的地址。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>下面我们用指针给出数组元素的地址和内容的几种表示形式：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1).&nbsp;p+i</SPAN>和<SPAN lang=EN-US>a+i</SPAN>均表示<SPAN lang=EN-US>a[i]</SPAN>的地址<SPAN lang=EN-US>,&nbsp;</SPAN>或者讲<SPAN lang=EN-US>,</SPAN>它们均指向数组第<SPAN lang=EN-US>i</SPAN>号元素<SPAN lang=EN-US>,&nbsp;</SPAN>即指向<SPAN lang=EN-US>a[i]</SPAN>。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(2).&nbsp;*(p+i)</SPAN>和<SPAN lang=EN-US>*(a+i)</SPAN>都表示<SPAN lang=EN-US>p+i</SPAN>和<SPAN lang=EN-US>a+i</SPAN>所指对象的内容<SPAN lang=EN-US>,</SPAN>即为<SPAN lang=EN-US>a[i]</SPAN>。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(3).&nbsp;</SPAN>指向数组元素的指针<SPAN lang=EN-US>,&nbsp;</SPAN>也可以表示成数组的形式<SPAN lang=EN-US>,</SPAN>也就是说<SPAN lang=EN-US>,</SPAN>它允许指针变量带下标<SPAN lang=EN-US>,&nbsp;</SPAN>如<SPAN lang=EN-US>p[i]</SPAN>与<SPAN lang=EN-US>*(p+i)</SPAN>等价。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>假若<SPAN lang=EN-US>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p=a+5;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>则<SPAN lang=EN-US>p[2]</SPAN>就相当于<SPAN lang=EN-US>*(p+2),&nbsp;</SPAN>由于<SPAN lang=EN-US>p</SPAN>指向<SPAN lang=EN-US>a[5],&nbsp;</SPAN>所以<SPAN lang=EN-US>p[2]</SPAN>就相当于<SPAN lang=EN-US>a[7]</SPAN>。而<SPAN lang=EN-US>p[-3]</SPAN>就相当于<SPAN lang=EN-US>*(p-3),&nbsp;</SPAN>它表示<SPAN lang=EN-US>a[2]</SPAN>。<SPAN lang=EN-US><BR><BR></SPAN>二、指向二维数组的指针<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;1.</SPAN>二维数组元素的地址<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>为了说明问题<SPAN lang=EN-US>,&nbsp;</SPAN>我们定义以下二维数组<SPAN lang=EN-US>:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a[3][4]={{0,1,2,3},&nbsp;{4,5,6,7},&nbsp;{8,9,10,11}};&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a</SPAN>为二维数组名<SPAN lang=EN-US>,</SPAN>此数组有<SPAN lang=EN-US>3</SPAN>行<SPAN lang=EN-US>4</SPAN>列<SPAN lang=EN-US>,&nbsp;</SPAN>共<SPAN lang=EN-US>12</SPAN>个元素。但也可这样来理解<SPAN lang=EN-US>,</SPAN>数组<SPAN lang=EN-US>a</SPAN>由三个元素组成<SPAN lang=EN-US>:a[0],a[1],a[2]</SPAN>。而每个元素又是一个一维数组<SPAN lang=EN-US>,&nbsp;</SPAN>且都含有<SPAN lang=EN-US>4</SPAN>个元素<SPAN lang=EN-US>(</SPAN>相当于<SPAN lang=EN-US>4</SPAN>列<SPAN lang=EN-US>),</SPAN>例如<SPAN lang=EN-US>,a[0]</SPAN>所代表的一维数组所包含的<SPAN lang=EN-US>&nbsp;4&nbsp;</SPAN>个元素为<SPAN lang=EN-US>a[0][0],&nbsp;a[0][1],&nbsp;a[0][2],&nbsp;a[0][3]</SPAN>。如图所示<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;______&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_______________<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a---|&nbsp;a[0]&nbsp;|&nbsp;____&nbsp;|&nbsp;0&nbsp;|&nbsp;1&nbsp;|&nbsp;2&nbsp;|&nbsp;3&nbsp;|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|______|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;a[1]&nbsp;|&nbsp;____&nbsp;|&nbsp;4&nbsp;|&nbsp;5&nbsp;|&nbsp;6&nbsp;|&nbsp;7&nbsp;|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|______|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;a[2]&nbsp;|&nbsp;____&nbsp;|&nbsp;8&nbsp;|&nbsp;9&nbsp;|&nbsp;10|&nbsp;11|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|______|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>但从二维数组的角度来看<SPAN lang=EN-US>,a</SPAN>代表二维数组的首地址<SPAN lang=EN-US>,</SPAN>当然也可看成是二维数组第<SPAN lang=EN-US>0</SPAN>行的首地址。<SPAN lang=EN-US>a+1</SPAN>就代表第<SPAN lang=EN-US>1</SPAN>行的首地址<SPAN lang=EN-US>,a+2</SPAN>就代表第<SPAN lang=EN-US>2</SPAN>行的首地址。如果此二维数组的首地址为<SPAN lang=EN-US>1000,</SPAN>由于第<SPAN lang=EN-US>0</SPAN>行有<SPAN lang=EN-US>4</SPAN>个整型元素<SPAN lang=EN-US>,</SPAN>所以<SPAN lang=EN-US>a+1</SPAN>为<SPAN lang=EN-US>1008,a+2</SPAN>也就为<SPAN lang=EN-US>1016</SPAN>。如图所示<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_______________<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1000)&nbsp;____&nbsp;|&nbsp;0&nbsp;|&nbsp;1&nbsp;|&nbsp;2&nbsp;|&nbsp;3&nbsp;|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1008)&nbsp;____&nbsp;|&nbsp;4&nbsp;|&nbsp;5&nbsp;|&nbsp;6&nbsp;|&nbsp;7&nbsp;|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1016)&nbsp;____&nbsp;|&nbsp;8&nbsp;|&nbsp;9&nbsp;|&nbsp;10|&nbsp;11|<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>既然我们把<SPAN lang=EN-US>a[0],a[1],a[2]</SPAN>看成是一维数组名<SPAN lang=EN-US>,</SPAN>可以认为它们分别代表它们所对应的数组的首地址<SPAN lang=EN-US>,</SPAN>也就是讲<SPAN lang=EN-US>,a[0]</SPAN>代表第<SPAN lang=EN-US>&nbsp;0&nbsp;</SPAN>行中第<SPAN lang=EN-US>&nbsp;0&nbsp;</SPAN>列元素的地址<SPAN lang=EN-US>,</SPAN>即<SPAN lang=EN-US>&amp;a[0][0],&nbsp;a[1]</SPAN>是第<SPAN lang=EN-US>1</SPAN>行中第<SPAN lang=EN-US>0</SPAN>列元素的地址<SPAN lang=EN-US>,</SPAN>即<SPAN lang=EN-US>&amp;a[1][0],</SPAN>根据地址运算规则<SPAN lang=EN-US>,a[0]+1</SPAN>即代表第<SPAN lang=EN-US>0</SPAN>行第<SPAN lang=EN-US>1</SPAN>列元素的地址<SPAN lang=EN-US>,</SPAN>即<SPAN lang=EN-US>&amp;a[0][1],</SPAN>一般而言<SPAN lang=EN-US>,a[i]+j</SPAN>即代表第<SPAN lang=EN-US>i</SPAN>行第<SPAN lang=EN-US>j</SPAN>列元素的地址<SPAN lang=EN-US>,&nbsp;</SPAN>即<SPAN lang=EN-US>&amp;a[i][j]</SPAN>。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>另外<SPAN lang=EN-US>,</SPAN>在二维数组中<SPAN lang=EN-US>,</SPAN>我们还可用指针的形式来表示各元素的地址。如前所述<SPAN lang=EN-US>,a[0]</SPAN>与<SPAN lang=EN-US>*(a+0)</SPAN>等价<SPAN lang=EN-US>,a[1]</SPAN>与<SPAN lang=EN-US>*(a+1)</SPAN>等价<SPAN lang=EN-US>,</SPAN>因此<SPAN lang=EN-US>a[i]+j</SPAN>就与<SPAN lang=EN-US>*(a+i)+j</SPAN>等价<SPAN lang=EN-US>,</SPAN>它表示数组元素<SPAN lang=EN-US>a[i][j]</SPAN>的地址。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>因此<SPAN lang=EN-US>,</SPAN>二维数组元素<SPAN lang=EN-US>a[i][j]</SPAN>可表示成<SPAN lang=EN-US>*(a[i]+j)</SPAN>或<SPAN lang=EN-US>*(*(a+i)+j),</SPAN>它们都与<SPAN lang=EN-US>a[i][j]</SPAN>等价<SPAN lang=EN-US>,</SPAN>或者还可写成<SPAN lang=EN-US>(*(a+i))[j]</SPAN>。<SPAN lang=EN-US>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>另外<SPAN lang=EN-US>,&nbsp;</SPAN>要补充说明一下<SPAN lang=EN-US>,&nbsp;</SPAN>果你编写一个程序输出打印<SPAN lang=EN-US>a</SPAN>和<SPAN lang=EN-US>*a,</SPAN>你可发现它们的值是相同的<SPAN lang=EN-US>,</SPAN>这是为什么呢<SPAN lang=EN-US>?&nbsp;</SPAN>我们可这样来理解<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>首先<SPAN lang=EN-US>,</SPAN>为了说明问题<SPAN lang=EN-US>,</SPAN>我们把二维数组人为地看成由三个数组元素<SPAN lang=EN-US>a[0],a[1],a[2]</SPAN>组成<SPAN lang=EN-US>,</SPAN>将<SPAN lang=EN-US>a[0],a[1],a[2]</SPAN>看成是数组名它们又分别是由<SPAN lang=EN-US>4</SPAN>个元素组成的一维数组。因此<SPAN lang=EN-US>,a</SPAN>表示数组第<SPAN lang=EN-US>0</SPAN>行的地址<SPAN lang=EN-US>,&nbsp;</SPAN>而<SPAN lang=EN-US>*a</SPAN>即为<SPAN lang=EN-US>a[0],&nbsp;</SPAN>它是数组名<SPAN lang=EN-US>,&nbsp;</SPAN>当然还是地址<SPAN lang=EN-US>,</SPAN>它就是数组第<SPAN lang=EN-US>0&nbsp;</SPAN>行第<SPAN lang=EN-US>0&nbsp;</SPAN>列元素的地址。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;2.</SPAN>指向一个由<SPAN lang=EN-US>n</SPAN>个元素所组成的数组指针<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>在<SPAN lang=EN-US>Turbo&nbsp;C</SPAN>中<SPAN lang=EN-US>,&nbsp;</SPAN>可定义如下的指针变量<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*p)[3];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针<SPAN lang=EN-US>p</SPAN>为指向一个由<SPAN lang=EN-US>3</SPAN>个元素所组成的整型数组指针。在定义中<SPAN lang=EN-US>,</SPAN>圆括号是不能少的<SPAN lang=EN-US>,&nbsp;</SPAN>否则它是指针数组<SPAN lang=EN-US>,&nbsp;</SPAN>这将在后面介绍。这种数组的指针不同于前面介绍的整型指针<SPAN lang=EN-US>,</SPAN>当整型指针指向一个整型数组的元素时<SPAN lang=EN-US>,</SPAN>进行指针<SPAN lang=EN-US>(</SPAN>地址<SPAN lang=EN-US>)</SPAN>加<SPAN lang=EN-US>1</SPAN>运算<SPAN lang=EN-US>,</SPAN>表示指向数组的下一个元素<SPAN lang=EN-US>,</SPAN>此时地址值增加了<SPAN lang=EN-US>2(</SPAN>因为放大因子为<SPAN lang=EN-US>2),</SPAN>而如上所定义的指向一个由<SPAN lang=EN-US>3</SPAN>个元素组成的数组指针<SPAN lang=EN-US>,</SPAN>进行地址加<SPAN lang=EN-US>1</SPAN>运算时<SPAN lang=EN-US>,</SPAN>其地址值增加了<SPAN lang=EN-US>6(</SPAN>放大因子为<SPAN lang=EN-US>2x3=6),&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>这种数组指针在<SPAN lang=EN-US>Turbo&nbsp;C</SPAN>中用得较少<SPAN lang=EN-US>,</SPAN>但在处理二维数组时<SPAN lang=EN-US>,&nbsp;</SPAN>还是很方便的。例如<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a[3][4],&nbsp;(*p)[4];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p=a;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>开始时<SPAN lang=EN-US>p</SPAN>指向二维数组第<SPAN lang=EN-US>0</SPAN>行<SPAN lang=EN-US>,</SPAN>当进行<SPAN lang=EN-US>p+1</SPAN>运算时<SPAN lang=EN-US>,</SPAN>根据地址运算规则<SPAN lang=EN-US>,</SPAN>此时放大因子为<SPAN lang=EN-US>4x2=8,</SPAN>所以此时正好指向二维数组的第<SPAN lang=EN-US>1</SPAN>行。和二维数组元素地址计算的规则一样<SPAN lang=EN-US>,*p+1</SPAN>指向<SPAN lang=EN-US>a[0][1],*(p+i)+j</SPAN>则指向数组元素<SPAN lang=EN-US>a[i][j]</SPAN>。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例：<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;a[3][4]={&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{1,3,5,7},&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{9,11,13,15},&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{17,19,21,23}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i,(*b)[4];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b=a+1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;b</SPAN>指向二维数组的第<SPAN lang=EN-US>1</SPAN>行<SPAN lang=EN-US>,&nbsp;</SPAN>此时<SPAN lang=EN-US>*b[0]</SPAN>是<SPAN lang=EN-US>a[1][0]&nbsp;*/&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i=1;i&lt;=4;b=b[0]+2,i++)&nbsp;&nbsp;&nbsp;/*&nbsp;</SPAN>修改<SPAN lang=EN-US>b</SPAN>的指向<SPAN lang=EN-US>,&nbsp;</SPAN>每次增加<SPAN lang=EN-US>2&nbsp;*/&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\t",*b[0]);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("\n");&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i=0;&nbsp;i&lt;3;&nbsp;i++)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b=a+i;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;</SPAN>修改<SPAN lang=EN-US>b</SPAN>的指向<SPAN lang=EN-US>,</SPAN>每次跳过二维数组的一行<SPAN lang=EN-US>&nbsp;*/&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%d\t",*(b[i]+1));&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf&nbsp;("\n");&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>程序运行结果如下<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9&nbsp;&nbsp;&nbsp;&nbsp;13&nbsp;&nbsp;&nbsp;17&nbsp;&nbsp;&nbsp;21&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;&nbsp;11&nbsp;&nbsp;&nbsp;19&nbsp;<BR><BR></SPAN>三、字符指针<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>我们已经知道<SPAN lang=EN-US>,</SPAN>字符串常量是由双引号括起来的字符序列<SPAN lang=EN-US>,</SPAN>例如<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"a&nbsp;string"&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>就是一个字符串常量<SPAN lang=EN-US>,</SPAN>该字符串中因为字符<SPAN lang=EN-US>a</SPAN>后面还有一个空格字符<SPAN lang=EN-US>,</SPAN>所以它由<SPAN lang=EN-US>8</SPAN>个字符序列组成。在程序中如出现字符串常量<SPAN lang=EN-US>C</SPAN>编译程序就给字符串常量按排一存贮区域<SPAN lang=EN-US>,</SPAN>这个区域是静态的<SPAN lang=EN-US>,</SPAN>在整个程序运行的过程中始终占用<SPAN lang=EN-US>,&nbsp;</SPAN>平时所讲的字符串常量的长度是指该字符串的字符个数<SPAN lang=EN-US>,&nbsp;</SPAN>但在按排存贮区域时<SPAN lang=EN-US>,&nbsp;C&nbsp;</SPAN>编译程序还自动给该字符串序列的末尾加上一个空字符<SPAN lang=EN-US>'\0',</SPAN>用来标志字符串的结束<SPAN lang=EN-US>,</SPAN>因此一个字符串常量所占的存贮区域的字节数总比它的字符个数多一个字节。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;Turbo&nbsp;C</SPAN>中操作一个字符串常量的方法有<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;(1).</SPAN>把字符串常量存放在一个字符数组之中<SPAN lang=EN-US>,&nbsp;</SPAN>例如<SPAN lang=EN-US>:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;s[]="a&nbsp;string";&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>数组<SPAN lang=EN-US>s</SPAN>共有<SPAN lang=EN-US>9</SPAN>个元素所组成<SPAN lang=EN-US>,</SPAN>其中<SPAN lang=EN-US>s[8]</SPAN>中的内容是<SPAN lang=EN-US>'\0'</SPAN>。实际上<SPAN lang=EN-US>,</SPAN>在字符数组定义的过程中<SPAN lang=EN-US>,</SPAN>编译程序直接把字符串复写到数组中<SPAN lang=EN-US>,</SPAN>即对数组<SPAN lang=EN-US>s</SPAN>初始化。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;(2).</SPAN>用字符指针指向字符串<SPAN lang=EN-US>,</SPAN>然后通过字符指针来访问字符串存贮区域。当字符串常量在表达式中出现时<SPAN lang=EN-US>,&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>根据数组的类型转换规则<SPAN lang=EN-US>,</SPAN>它被转换成字符指针。因此<SPAN lang=EN-US>,</SPAN>若我们定义了一字符指针<SPAN lang=EN-US>cp:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*cp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>于是可用<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cp="a&nbsp;string";&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>使<SPAN lang=EN-US>cp</SPAN>指向字符串常量中的第<SPAN lang=EN-US>0</SPAN>号字符<SPAN lang=EN-US>a,&nbsp;</SPAN>如图所示。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;___________________________________<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CP&nbsp;-----&nbsp;&nbsp;&nbsp;|&nbsp;a&nbsp;|&nbsp;&nbsp;&nbsp;|&nbsp;s&nbsp;|&nbsp;t&nbsp;|&nbsp;r&nbsp;|&nbsp;i&nbsp;|&nbsp;n&nbsp;|&nbsp;g&nbsp;|&nbsp;\0|&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___|___|___|___|___|___|___|___|___|&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>以后我们可通过<SPAN lang=EN-US>cp</SPAN>来访问这一存贮区域<SPAN lang=EN-US>,&nbsp;</SPAN>如<SPAN lang=EN-US>*cp</SPAN>或<SPAN lang=EN-US>cp[0]</SPAN>就是字符<SPAN lang=EN-US>a,</SPAN>而<SPAN lang=EN-US>cp[i]</SPAN>或<SPAN lang=EN-US>*(cp+i)</SPAN>就相当于字符串的第<SPAN lang=EN-US>i</SPAN>号字符<SPAN lang=EN-US>,</SPAN>但企图通过指针来修改字符串常量的行为是没有意义的。<SPAN lang=EN-US>&nbsp;<BR><BR></SPAN>四、指针数组<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>因为指针是变量<SPAN lang=EN-US>,</SPAN>因此可设想用指向同一数据类型的指针来构成一个数组<SPAN lang=EN-US>,&nbsp;</SPAN>这就是指针数组。数组中的每个元素都是指针变量<SPAN lang=EN-US>,</SPAN>根据数组的定义<SPAN lang=EN-US>,</SPAN>指针数组中每个元素都为指向同一数据类型的指针。指针数组的定义格式为<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>类型标识<SPAN lang=EN-US>&nbsp;*</SPAN>数组名<SPAN lang=EN-US>[</SPAN>整型常量表达式<SPAN lang=EN-US>];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例如<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;*a[10];&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>定义了一个指针数组<SPAN lang=EN-US>,</SPAN>数组中的每个元素都是指向整型量的指针<SPAN lang=EN-US>,</SPAN>该数组由<SPAN lang=EN-US>10</SPAN>个元素组成<SPAN lang=EN-US>,</SPAN>即<SPAN lang=EN-US>a[0],a[1],a[2],&nbsp;...,&nbsp;a[9],</SPAN>它们均为指针变量。<SPAN lang=EN-US>a</SPAN>为该指针数组名<SPAN lang=EN-US>,</SPAN>和数组一样，<SPAN lang=EN-US>a</SPAN>是常量<SPAN lang=EN-US>,</SPAN>不能对它进行增量运算。<SPAN lang=EN-US>a</SPAN>为指针数组元素<SPAN lang=EN-US>a[0]</SPAN>的地址<SPAN lang=EN-US>,a+i</SPAN>为<SPAN lang=EN-US>a[i]</SPAN>的地址<SPAN lang=EN-US>,*a</SPAN>就是<SPAN lang=EN-US>a[0],*(a+i)</SPAN>就是<SPAN lang=EN-US>a[i]</SPAN>。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>为什么要定义和使用指针数组呢<SPAN lang=EN-US>?</SPAN>主要是由于指针数组对处理字符串提供了更大的方便和灵活<SPAN lang=EN-US>,</SPAN>使用二维数组对处理长度不等的正文效率低，而指针数组由于其中每个元素都为指针变量<SPAN lang=EN-US>,</SPAN>因此通过地址运算来操作正文行是十分方便的。<SPAN lang=EN-US><BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>指针数组和一般数组一样<SPAN lang=EN-US>,</SPAN>允许指针数组在定义时初始化，但由于指针数组的每个元素是指针变量<SPAN lang=EN-US>,</SPAN>它只能存放地址<SPAN lang=EN-US>,</SPAN>所以对指向字符串的指针数组在说明赋初值时<SPAN lang=EN-US>,</SPAN>是把存放字符串的首地址赋给指针数组的对应元素<SPAN lang=EN-US>,&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例如下面是一个书写函数<SPAN lang=EN-US>month_name(n),</SPAN>函数返回一个指向包含第<SPAN lang=EN-US>n</SPAN>月名字的字符指针<SPAN lang=EN-US>(</SPAN>关于函数指针和指针函数<SPAN lang=EN-US>,</SPAN>下一节将专门介绍<SPAN lang=EN-US>)</SPAN>。<SPAN lang=EN-US>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN>例<SPAN lang=EN-US>:&nbsp;</SPAN>打印<SPAN lang=EN-US>1</SPAN>月至<SPAN lang=EN-US>12</SPAN>月的月名<SPAN lang=EN-US>:&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;*month_name(int&nbsp;n)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;char&nbsp;*name[]={&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Illegal&nbsp;month",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"January",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"February",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"March",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"April",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"May",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"June",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"July",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"August",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"September",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"October",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"November",&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"December"&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return((n&lt;1||n&gt;12)?name[0]:name[n]);&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main()&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;i;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i=0;&nbsp;i&lt;13;&nbsp;i++)&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("%s\n",&nbsp;month_name(i));&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR style="mso-special-character: line-break"><BR style="mso-special-character: line-break"><SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">对于指针这一节，一定要多练习一些题。指针是一个很重要的概念，必须多接触实际的问题才能掌握它。</SPAN></SPAN></SPAN><img src ="http://www.cppblog.com/ivenher/aggbug/912.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-11-03 10:06 <a href="http://www.cppblog.com/ivenher/articles/912.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎么判断_variant_t的值是什么类型</title><link>http://www.cppblog.com/ivenher/articles/775.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 21 Oct 2005 06:42:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/775.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/775.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/775.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/775.html</trackback:ping><description><![CDATA[/*<BR>将 _variant_t 类型的变量，转换成字符串（比如如果是数字，则转换成数字对应的字符串 ：如 10.2 -&gt; "10.200000"）<BR><BR>*/<BR><BR>_variant_t var;&nbsp; <BR><BR>CString str; //转换以后的字符串<BR><BR><BR><BR>//以下代码演示如何转换为C标准字符串型<BR>if (var.vt == VT_I4)<BR>{<BR>&nbsp;&nbsp;&nbsp; long lNum;<BR>&nbsp;&nbsp;&nbsp; char szCh[21];<BR>&nbsp;&nbsp;&nbsp; str=var.bstrVal;<BR>&nbsp;&nbsp;&nbsp; WideCharToMultiByte <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (CP_ACP, 0, var.bstrVal, -1,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; szCh, sizeof(szCh), NULL, NULL);<BR>}<BR><BR>//以下代码演示如何转换成逻辑型<BR>if( var.vt == VT_BOOL)<BR>{<BR>&nbsp;&nbsp;&nbsp; BOOL bVar;<BR>&nbsp;&nbsp;&nbsp; lNum=var.lVal;<BR>&nbsp;&nbsp;&nbsp; bVar= var.boolVar==0? FALSE : TRUE;<BR>}<BR><BR>//以下代码演示为其余类型（补充）<BR>switch(var.vt)<BR>{<BR><BR> case VT_BSTR:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is BSTR type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str=var.bstrVal;<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_I2:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is short int type <BR>&nbsp;&nbsp;&nbsp;&nbsp; str.Format("%d",(int)var.iVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_I4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is long int type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str.Format("%d",var.lVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_R4:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is float type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str.Format("%10.6f",(double)var.fltVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_R8:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is double type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str.Format("%10.6f",var.dblVal);<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_CY:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //var is CY type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str=COleCurrency(var).Format();<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_DATE:&nbsp;&nbsp;&nbsp;&nbsp; //var is DATE type<BR>&nbsp;&nbsp;&nbsp;&nbsp; str=COleDateTime(var).Format();<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> case VT_BOOL:&nbsp;&nbsp;&nbsp;&nbsp; //var is&nbsp; VARIANT_BOOL<BR>&nbsp;&nbsp;&nbsp;&nbsp; str= (var.boolVal==0) ?"FALSE": "TRUE";<BR>&nbsp;&nbsp;&nbsp;&nbsp; break;<BR><BR> default:<BR>&nbsp;&nbsp;&nbsp;&nbsp; str.Format("Unk type %d\n",var.vt);<BR>&nbsp;&nbsp;&nbsp;&nbsp; TRACE("Unknown type %d\n",var.vt);<BR>}<BR><img src ="http://www.cppblog.com/ivenher/aggbug/775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-10-21 14:42 <a href="http://www.cppblog.com/ivenher/articles/775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++字符串完全指引之一 —— Win32 字符编码（转）</title><link>http://www.cppblog.com/ivenher/articles/773.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 21 Oct 2005 06:27:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/773.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/773.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/773.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/773.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<P align=center><B>C++字符串完全指引之一 —— Win32 字符编码<BR><BR></B><BR>原著：Michael Dunn<BR><BR>翻译：<A href="mailto:cjsun@insun.hit.edu.cn"><FONT color=#4a664d>Chengjie Sun</FONT></A></P>
<P><BR><BR>原文出处：<FONT color=#4a664d size=2><A href="http://www.codeproject.com/string/cppstringguide1.asp" target=_blank>CodeProject：The Complete Guide to C++ Strings, Part I</A></FONT><BR><BR><FONT color=#4a664d><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"></FONT> <B>引言</B><BR><BR>　　毫无疑问，我们都看到过像 TCHAR, std::string, BSTR 等各种各样的字符串类型，还有那些以 _tcs 开头的奇怪的宏。你也许正在盯着显示器发愁。本指引将总结引进各种字符类型的目的，展示一些简单的用法，并告诉您在必要时，如何实现各种字符串类型之间的转换。<BR>　　在第一部分，我们将介绍3种字符编码类型。了解各种编码模式的工作方式是很重要的事情。即使你已经知道一个字符串是一个字符数组，你也应该阅读本部分。一旦你了解了这些，你将对各种字符串类型之间的关系有一个清楚地了解。<BR>　　在第二部分，我们将单独讲述string类，怎样使用它及实现他们相互之间的转换。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>字符基础 -- ASCII, DBCS, Unicode</B><BR><BR>　　所有的 string 类都是以C-style字符串为基础的。C-style 字符串是字符数组。所以我们先介绍字符类型。这里有3种编码模式对应3种字符类型。第一种编码类型是单子节字符集（single-byte character set or SBCS）。在这种编码模式下，所有的字符都只用一个字节表示。ASCII是SBCS。一个字节表示的0用来标志SBCS字符串的结束。<BR>　　第二种编码模式是多字节字符集（multi-byte character set or MBCS）。一个MBCS编码包含一些一个字节长的字符，而另一些字符大于一个字节的长度。用在Windows里的MBCS包含两种字符类型，单字节字符（single-byte characters）和双字节字符（double-byte characters）。由于Windows里使用的多字节字符绝大部分是两个字节长，所以MBCS常被用DBCS代替。<BR>　　在DBCS编码模式中，一些特定的值被保留用来表明他们是双字节字符的一部分。例如，在Shift-JIS编码中（一个常用的日文编码模式），0x81-0x9f之间和 0xe0-oxfc之间的值表示"这是一个双字节字符，下一个子节是这个字符的一部分。"这样的值被称作"leading bytes",他们都大于0x7f。跟随在一个leading byte子节后面的字节被称作"trail byte"。在DBCS中，trail byte可以是任意非0值。像SBCS一样，DBCS字符串的结束标志也是一个单字节表示的0。<BR>　　第三种编码模式是Unicode。Unicode是一种所有的字符都使用两个字节编码的编码模式。Unicode字符有时也被称作宽字符，因为它比单子节字符宽（使用了更多的存储空间）。注意，Unicode不能被看作MBCS。MBCS的独特之处在于它的字符使用不同长度的字节编码。Unicode字符串使用两个字节表示的0作为它的结束标志。<BR>　　单字节字符包含拉丁文字母表，accented characters及ASCII标准和DOS操作系统定义的图形字符。双字节字符被用来表示东亚及中东的语言。Unicode被用在COM及Windows NT操作系统内部。<BR>　　你一定已经很熟悉单字节字符。当你使用char时，你处理的是单字节字符。双字节字符也用char类型来进行操作（这是我们将会看到的关于双子节字符的很多奇怪的地方之一）。Unicode字符用wchar_t来表示。Unicode字符和字符串常量用前缀L来表示。例如：</P><PRE>wchar_t wch = L''1''; // 2 bytes, 0x0031
wchar_t* wsz = L"Hello"; // 12 bytes, 6 wide characters</PRE>
<P><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>字符在内存中是怎样存储的</B><BR><BR>　　单字节字符串：每个字符占一个字节按顺序依次存储，最后以单字节表示的0结束。例如。"Bob"的存贮形式如下：</P>
<TABLE id=AutoNumber1 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="26%" border=1>
<TBODY>
<TR>
<TD align=middle width="25%">42</TD>
<TD align=middle width="25%"><FONT color=#990000>6F</FONT></TD>
<TD align=middle width="25%">62</TD>
<TD align=middle width="25%">00</TD></TR>
<TR>
<TD align=middle width="25%"><FONT color=#990000>B</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>o</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>b</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>BOS</FONT></TD></TR></TBODY></TABLE>
<P>Unicode的存储形式，L"Bob"</P>
<TABLE id=AutoNumber2 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="42%" border=1>
<TBODY>
<TR>
<TD align=middle width="25%">42 00 </TD>
<TD align=middle width="25%"><FONT color=#990000>6F</FONT> 00</TD>
<TD align=middle width="25%">62 00</TD>
<TD align=middle width="25%">00 00</TD></TR>
<TR>
<TD align=middle width="25%"><FONT color=#990000>B</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>o</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>b</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>BOS</FONT></TD></TR></TBODY></TABLE>
<P>使用两个字节表示的0来做结束标志。<BR><BR>　　一眼看上去，DBCS 字符串很像 SBCS 字符串，但是我们一会儿将看到 DBCS 字符串的微妙之处，它使得使用字符串操作函数和永字符指针遍历一个字符串时会产生预料之外的结果。字符串" " ("nihongo")在内存中的存储形式如下（LB和TB分别用来表示 leading byte 和 trail byte）</P>
<TABLE id=AutoNumber3 style="BORDER-COLLAPSE: collapse" borderColor=#111111 height=52 cellSpacing=0 cellPadding=0 width="62%" border=1>
<TBODY>
<TR>
<TD align=middle width="25%" height=20>93 <FONT color=#990000>FA</FONT></TD>
<TD align=middle width="25%" height=20>96 <FONT color=#990000>7B</FONT></TD>
<TD align=middle width="25%" height=20><FONT color=#990000>8C EA</FONT></TD>
<TD align=middle width="25%" height=20>00</TD></TR>
<TR>
<TD align=middle width="25%" height=20><FONT color=#990000>LB TB</FONT></TD>
<TD align=middle width="25%" height=20><FONT color=#990000>LB TB</FONT></TD>
<TD align=middle width="25%" height=20><FONT color=#990000>LB TB</FONT></TD>
<TD align=middle width="25%" height=20><FONT color=#990000>EOS</FONT></TD></TR>
<TR>
<TD align=middle width="25%" height=20><IMG height=13 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/ri.gif" width=10 border=0></TD>
<TD align=middle width="25%" height=20><IMG height=13 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/ben.gif" width=13 border=0></TD>
<TD align=middle width="25%" height=20><IMG height=13 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/yu.gif" width=13 border=0></TD>
<TD align=middle width="25%" height=20><FONT color=#990000>EOS</FONT></TD></TR></TBODY></TABLE>
<P>值得注意的是，"ni"的值不能被解释成WORD型值0xfa93，而应该看作两个值93和fa以这种顺序被作为"ni"的编码。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>使用字符串处理函数</B><BR><BR>　　我们都已经见过C语言中的字符串函数，strcpy(), sprintf(), atoll()等。这些字符串只应该用来处理单字节字符字符串。标准库也提供了仅适用于Unicode类型字符串的函数，比如wcscpy(), swprintf(), wtol()等。<BR>　　微软还在它的CRT(C runtime library)中增加了操作DBCS字符串的版本。Str***()函数都有对应名字的DBCS版本_mbs***()。如果你料到可能会遇到DBCS字符串（如果你的软件会被安装在使用DBCS编码的国家，如中国，日本等，你就可能会），你应该使用_mbs***()函数，因为他们也可以处理SBCS字符串。（一个DBCS字符串也可能含有单字节字符，这就是为什么_mbs***()函数也能处理SBCS字符串的原因）<BR>　　让我们来看一个典型的字符串来阐明为什么需要不同版本的字符串处理函数。我们还是使用前面的Unicode字符串 L"Bob"：</P>
<TABLE id=AutoNumber4 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="42%" border=1>
<TBODY>
<TR>
<TD align=middle width="25%">42 00 </TD>
<TD align=middle width="25%"><FONT color=#990000>6F</FONT> 00</TD>
<TD align=middle width="25%">62 00</TD>
<TD align=middle width="25%">00 00</TD></TR>
<TR>
<TD align=middle width="25%"><FONT color=#990000>B</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>o</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>b</FONT></TD>
<TD align=middle width="25%"><FONT color=#990000>BOS</FONT></TD></TR></TBODY></TABLE>
<P>　　因为x86CPU是little-endian，值0x0042在内存中的存储形式是42 00。你能看出如果这个字符串被传给strlen()函数会出现什么问题吗？它将先看到第一个字节42，然后是00，而00是字符串结束的标志，于是strlen()将会返回1。如果把"Bob"传给wcslen()，将会得出更坏的结果。wcslen()将会先看到0x6f42，然后是0x0062，然后一直读到你的缓冲区的末尾，直到发现00 00结束标志或者引起了GPF。<BR>　　到目前为止，我们已经讨论了str***()和wcs***()的用法及它们之间的区别。Str***()和_mbs**()之间的有区别区别呢？明白他们之间的区别，对于采用正确的方法来遍历DBCS字符串是很重要的。下面，我们将先介绍字符串的遍历，然后回到str***()与_mbs***()之间的区别这个问题上来。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>正确的遍历和索引字符串</B><BR><BR>　　因为我们中大多数人都是用着SBCS字符串成长的，所以我们在遍历字符串时，常常使用指针的++-和-操作。我们也使用数组下标的表示形式来操作字符串中的字符。这两种方式是用于SBCS和Unicode字符串，因为它们中的字符有着相同的宽度，编译器能正确的返回我们需要的字符。<BR>　　然而，当碰到DBCS字符串时，我们必须抛弃这些习惯。这里有使用指针遍历DBCS字符串时的两条规则。违背了这两条规则，你的程序就会存在DBCS有关的bugs。</P>
<DIR>
<LI>1．在前向遍历时，不要使用++操作，除非你每次都检查lead byte； 
<LI>2．永远不要使用-操作进行后向遍历。 </LI></DIR>
<P>　　我们先来阐述规则2，因为找到一个违背它的真实的实例代码是很容易的。假设你有一个程序在你自己的目录里保存了一个设置文件，你把安装目录保存在注册表中。在运行时，你从注册表中读取安装目录，然后合成配置文件名，接着读取该文件。假设，你的安装目录是C:\Program Files\MyCoolApp，那么你合成的文件名应该是C:\Program Files\MyCoolApp\config.bin。当你进行测试时，你发现程序运行正常。<BR>　　现在，想象你合成文件名的代码可能是这样的：</P><PRE>bool GetConfigFileName ( char* pszName, size_t nBuffSize )
{
    char szConfigFilename[MAX_PATH];
 
    // Read install dir from registry... we''ll assume it succeeds.
 
    // Add on a backslash if it wasn''t present in the registry value.
    // First, get a pointer to the terminating zero.
    char* pLastChar = strchr ( szConfigFilename, ''<!-- ~ Value_txtContent ~ -->'' );
 
    // Now move it back one character.
    pLastChar--;  
 
    if ( *pLastChar != ''\\'' )
        strcat ( szConfigFilename, "\\" );
 
    // Add on the name of the config file.
    strcat ( szConfigFilename, "config.bin" );
 
    // If the caller''s buffer is big enough, return the filename.
    if ( strlen ( szConfigFilename ) &gt;= nBuffSize )
        return false;
    else
        {
        strcpy ( pszName, szConfigFilename );
        return true;
        }
}      </PRE>　　这是一段很健壮的代码，然而在遇到 DBCS 字符时它将会出错。让我们来看看为什么。假设一个日本用户使用了你的程序，把它安装在 C:\<IMG height=13 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/youkoso.gif" width=35 border=0>。下面是这个名字在内存中的存储形式：<BR>　 
<TABLE id=AutoNumber5 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="55%" border=1>
<TBODY>
<TR>
<TD align=middle width="12%">43</TD>
<TD align=middle width="12%"><FONT color=#990000>3A</FONT></TD>
<TD align=middle width="12%"><FONT color=#0000ff>5C</FONT></TD>
<TD align=middle width="12%">83 88</TD>
<TD align=middle width="13%">83 45</TD>
<TD align=middle width="13%">83 52</TD>
<TD align=middle width="13%">83 <FONT color=#0000ff>5C</FONT></TD>
<TD align=middle width="13%">00</TD></TR>
<TR>
<TD align=middle width="12%">　</TD>
<TD align=middle width="12%">　</TD>
<TD align=middle width="12%">　</TD>
<TD align=middle width="12%"><FONT color=#990000>LB TB </FONT></TD>
<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD>
<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD>
<TD align=middle width="13%"><FONT color=#990000>LB TB </FONT></TD>
<TD align=middle width="13%">　</TD></TR>
<TR>
<TD align=middle width="12%">C</TD>
<TD align=middle width="12%">:</TD>
<TD align=middle width="12%">\</TD>
<TD align=middle width="12%"><IMG height=11 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/yo.gif" width=9 border=0></TD>
<TD align=middle width="13%"><IMG height=13 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/u.gif" width=10 border=0></TD>
<TD align=middle width="13%"><IMG height=11 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/ko.gif" width=9 border=0></TD>
<TD align=middle width="13%"><IMG height=12 alt="" src="http://www.vckbase.com/document/journal/vckbase30/images/so.gif" width=10 border=0></TD>
<TD align=middle width="13%"><SPAN lang=EN-US style="FONT-SIZE: 12pt; COLOR: #990000; FONT-FAMILY: Courier New">EOS</SPAN></TD></TR></TBODY></TABLE>
<P>　　当使用 GetConfigFileName() 检查尾部的''\\''时，它寻找安装目录名中最后的非0字节，看它是等于''\\''的，所以没有重新增加一个''\\''。结果是代码返回了错误的文件名。<BR>　　哪里出错了呢？看看上面两个被用蓝色高量显示的字节。斜杠''\\''的值是0x5c。'' ''的值是83 5c。上面的代码错误的读取了一个 trail byte，把它当作了一个字符。<BR>　　正确的后向遍历方法是使用能够识别DBCS字符的函数，使指针移动正确的字节数。下面是正确的代码。（指针移动的地方用红色标明） </P><PRE>bool FixedGetConfigFileName ( char* pszName, size_t nBuffSize )
{
    char szConfigFilename[MAX_PATH];
 
    // Read install dir from registry... we''ll assume it succeeds.
 
    // Add on a backslash if it wasn''t present in the registry value.
    // First, get a pointer to the terminating zero.
    char* pLastChar = _mbschr ( szConfigFilename, ''<!-- ~ Value_txtContent ~ -->'' );
 
    // Now move it back one double-byte character.
  <FONT color=#ff0000>  pLastChar = CharPrev ( szConfigFilename, pLastChar );</FONT>
 
    if ( *pLastChar != ''\\'' )
        _mbscat ( szConfigFilename, "\\" );
 
    // Add on the name of the config file.
    _mbscat ( szConfigFilename, "config.bin" );

     // If the caller''s buffer is big enough, return the filename.
    if ( _mbslen ( szInstallDir ) &gt;= nBuffSize )
        return false;
    else
        {
        _mbscpy ( pszName, szConfigFilename );
        return true;
        }
}
</PRE>　　上面的函数使用CharPrev() API使pLastChar向后移动一个字符，这个字符可能是两个字节长。在这个版本里，if条件正常工作，因为lead byte永远不会等于0x5c。<BR>　　让我们来想象一个违背规则1的场合。例如，你可能要检测一个用户输入的文件名是否多次出现了'':''。如果，你使用++操作来遍历字符串，而不是使用CharNext()，你可能会发出不正确的错误警告如果恰巧有一个trail byte它的值的等于'':''的值。<BR>与规则2相关的关于字符串索引的规则：<PRE>2a. 永远不要使用减法去得到一个字符串的索引。</PRE>
<P>违背这条规则的代码和违背规则2的代码很相似。例如，</P><PRE>char* pLastChar = &amp;szConfigFilename [strlen(szConfigFilename) - 1];</PRE>
<P>这和向后移动一个指针是同样的效果。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> 回到关于str***()和_mbs***()的区别</B><BR><BR>　　现在，我们应该很清楚为什么_mbs***()函数是必需的。Str***()函数根本不考虑DBCS字符，而_mbs***()考虑。如果，你调用strrchr("C:\\ ", ''\\'')，返回结果可能是错误的，然而_mbsrchr()将会认出最后的双字节字符，返回一个指向真的''\\''的指针。<BR>　　关于字符串函数的最后一点：str***()和_mbs***()函数认为字符串的长度都是以char来计算的。所以，如果一个字符串包含3个双字节字符，_mbslen()将会返回6。Unicode函数返回的长度是按wchar_t来计算的。例如，wcslen(L"Bob")返回3。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> Win32 API中的MBCS和Unicode</B><BR><BR>两组 APIs： <BR>　　尽管你也许从来没有注意过，Win32中的每个与字符串相关的API和message都有两个版本。一个版本接受MBCS字符串，另一个接受Unicode字符串。例如，根本没有SetWindowText()这个API，相反，有SetWindowTextA()和SetWindowTextW()。后缀A表明这是MBCS函数，后缀W表示这是Unicode版本的函数。<BR>　　当你 build 一个 Windows 程序，你可以选择是用 MBCS 或者 Unicode APIs。如果，你曾经用过VC向导并且没有改过预处理的设置，那表明你用的是MBCS版本。那么，既然没有 SetWindowText() API，我们为什么可以使用它呢？winuser.h头文件包含了一些宏，例如： </P><PRE>BOOL WINAPI SetWindowTextA ( HWND hWnd, LPCSTR lpString );
BOOL WINAPI SetWindowTextW ( HWND hWnd, LPCWSTR lpString );
 
#ifdef UNICODE
#define SetWindowText  SetWindowTextW
#else
#define SetWindowText  SetWindowTextA
#endif      </PRE>当使用MBCS APIs来build程序时，UNICODE没有被定义，所以预处理器看到：<PRE>#define SetWindowText SetWindowTextA</PRE>
<P>　　这个宏定义把所有对SetWindowText的调用都转换成真正的API函数SetWindowTextA。（当然，你可以直接调用SetWindowTextA() 或者 SetWindowTextW()，虽然你不必那么做。）<BR>　　所以，如果你想把默认使用的API函数变成Unicode版的，你可以在预处理器设置中，把_MBCS从预定义的宏列表中删除，然后添加UNICODE和_UNICODE。(你需要两个都定义，因为不同的头文件可能使用不同的宏。) 然而，如果你用char来定义你的字符串，你将会陷入一个尴尬的境地。考虑下面的代码：</P><PRE>HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";
SetWindowText ( hwnd, szNewText );</PRE>
<P>在预处理器把SetWindowText用SetWindowTextW来替换后，代码变成：</P><PRE>HWND hwnd = GetSomeWindowHandle();
char szNewText[] = "we love Bob!";
SetWindowTextW ( hwnd, szNewText );</PRE>
<P>　　看到问题了吗？我们把单字节字符串传给了一个以Unicode字符串做参数的函数。解决这个问题的第一个方案是使用 #ifdef 来包含字符串变量的定义：</P><PRE>HWND hwnd = GetSomeWindowHandle();
#ifdef UNICODE
wchar_t szNewText[] = L"we love Bob!";
#else
char szNewText[] = "we love Bob!";
#endif
SetWindowText ( hwnd, szNewText );</PRE>
<P>你可能已经感受到了这样做将会使你多么的头疼。完美的解决方案是使用TCHAR.<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> 使用TCHAR</B><BR><BR>　　TCHAR是一种字符串类型，它让你在以MBCS和UNNICODE来build程序时可以使用同样的代码，不需要使用繁琐的宏定义来包含你的代码。TCHAR的定义如下：</P><PRE>#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif</PRE>
<P>所以用MBCS来build时，TCHAR是char，使用UNICODE时，TCHAR是wchar_t。还有一个宏来处理定义Unicode字符串常量时所需的L前缀。</P><PRE>#ifdef UNICODE
#define _T(x) L##x
#else
#define _T(x) x
#endif</PRE>
<P>　　##是一个预处理操作符，它可以把两个参数连在一起。如果你的代码中需要字符串常量，在它前面加上_T宏。如果你使用Unicode来build，它会在字符串常量前加上L前缀。</P><PRE>TCHAR szNewText[] = _T("we love Bob!");</PRE>
<P>　　像是用宏来隐藏SetWindowTextA/W的细节一样，还有很多可以供你使用的宏来实现str***()和_mbs***()等字符串函数。例如，你可以使用_tcsrchr宏来替换strrchr()、_mbsrchr()和wcsrchr()。_tcsrchr根据你预定义的宏是_MBCS还是UNICODE来扩展成正确的函数，就像SetWindowText所作的一样。<BR>　　不仅str***()函数有TCHAR宏。其他的函数如， _stprintf（代替sprinft()和swprintf()）,_tfopen（代替fopen()和_wfopen()）。 MSDN中"Generic-Text Routine Mappings."标题下有完整的宏列表。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> 字符串和TCHAR typedefs</B><BR><BR>　　由于Win32 API文档的函数列表使用函数的常用名字（例如，"SetWindowText"），所有的字符串都是用TCHAR来定义的。（除了XP中引入的只适用于Unicode的API）。下面列出一些常用的typedefs，你可以在msdn中看到他们。</P>
<TABLE id=AutoNumber6 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=4 cellPadding=4 width="98%" border=1>
<TBODY>
<TR>
<TD align=middle width="16%"><B>type </B></TD>
<TD align=middle width="42%"><B>Meaning in MBCS builds </B></TD>
<TD align=middle width="42%"><B>Meaning in Unicode builds</B></TD></TR>
<TR>
<TD width="16%">WCHAR</TD>
<TD width="42%">wchar_t</TD>
<TD width="42%">wchar_t</TD></TR>
<TR>
<TD width="16%">LPSTR </TD>
<TD width="42%">zero-terminated string of char (char*)</TD>
<TD width="42%">zero-terminated string of char (char*)</TD></TR>
<TR>
<TD width="16%">LPCSTR </TD>
<TD width="42%">constant zero-terminated string of char (const char*)</TD>
<TD width="42%">constant zero-terminated string of char (const char*)</TD></TR>
<TR>
<TD width="16%">LPWSTR</TD>
<TD width="42%">zero-terminated Unicode string (wchar_t*) </TD>
<TD width="42%">zero-terminated Unicode string (wchar_t*)</TD></TR>
<TR>
<TD width="16%">LPCWSTR</TD>
<TD width="42%">constant zero-terminated Unicode string (const wchar_t*)</TD>
<TD width="42%">constant zero-terminated Unicode string (const wchar_t*) </TD></TR>
<TR>
<TD width="16%"><XXXXIME xime="7"></XXXXIME>TCHAR</TD>
<TD width="42%"><XXXXIME xime="7"></XXXXIME>char</TD>
<TD width="42%"><XXXXIME xime="7"></XXXXIME>wchar_t</TD></TR>
<TR>
<TD width="16%">LPTSTR</TD>
<TD width="42%">zero-terminated string of TCHAR (TCHAR*) </TD>
<TD width="42%">zero-terminated string of TCHAR (TCHAR*)</TD></TR>
<TR>
<TD width="16%">LPCTSTR </TD>
<TD width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</TD>
<TD width="42%">constant zero-terminated string of TCHAR (const TCHAR*)</TD></TR></TBODY></TABLE>
<P><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> 何时使用 TCHAR 和 Unicode</B><BR><BR>　　到现在，你可能会问，我们为什么要使用Unicode。我已经用了很多年的char。下列3种情况下，使用Unicode将会使你受益：</P>
<DIR>
<LI>1．你的程序只运行在Windows NT系统中。 
<LI>2． 你的程序需要处理超过MAX_PATH个字符长的文件名。 
<LI>3． 你的程序需要使用XP中引入的只有Unicode版本的API. </LI></DIR>
<P>　　Windows 9x 中大多数的 API 没有实现 Unicode 版本。所以，如果你的程序要在windows 9x中运行，你必须使用MBCS APIs。然而，由于NT系统内部都使用Unicode，所以使用Unicode APIs将会加快你的程序的运行速度。每次，你传递一个字符串调用MBCS API，操作系统会把这个字符串转换成Unicode字符串，然后调用对应的Unicode API。如果一个字符串被返回，操作系统还要把它转变回去。尽管这个转换过程被高度优化了，但它对速度造成的损失是无法避免的。<BR>　　只要你使用Unicode API，NT系统允许使用非常长的文件名（突破了MAX_PATH的限制，MAX_PATH=260）。使用Unicode API的另一个优点是你的程序会自动处理用户输入的各种语言。所以一个用户可以输入英文，中文或者日文，而你不需要额外编写代码去处理它们。<BR>　　最后，随着windows 9x产品的淡出，微软似乎正在抛弃MBCS APIs。例如，包含两个字符串参数的SetWindowTheme() API只有Unicode版本的。使用Unicode来build你的程序将会简化字符串的处理，你不必在MBCS和Unicdoe之间相互转换。<BR>　　即使你现在不使用Unicode来build你的程序，你也应该使用TCHAR及其相关的宏。这样做不仅可以的代码可以很好地处理DBCS，而且如果将来你想用Unicode来build你的程序，你只需要改变一下预处理器中的设置就可以实现了。<BR><BR></P>
<P></P>
<P></P>
<P></P></TD></TR>
<TR>
<TD><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"><B> 作者简介</B><BR>　　Michael Dunn：居住在阳光城市洛杉矶。他是如此的喜欢这里的天气以致于想一生都住在这里。他在4年级时开始编程，那时用的电脑是Apple //e。1995年，在 UCLA 获得数学学士学位，随后在Symantec 公司做 QA 工程师，在 Norton AntiVirus 组工作。他自学了 Windows 和 MFC 编程。1999-2000年，他设计并实现了 Norton AntiVirus 的新界面。　<BR>　　Michael 现在在 Napster（一个提供在线订阅音乐服务的公司）做开发工作，他还开发了UltraBar，一个IE工具栏插件，它可以使网络搜索更加容易，给了 googlebar 以沉重打击；他还开发了 CodeProject SearchBar；与人共同创建了 Zabersoft 公司，该公司在洛杉矶和丹麦的 Odense 都设有办事处。<BR>　　他喜欢玩游戏。爱玩的游戏有 pinball, bike riding，偶尔还玩 PS, Dreamcasth 和 MAME 游戏。他因忘了自己曾经学过的语言：法语、汉语、日语而感到悲哀。</TD></TR></TBODY></TABLE><BR>
<DIV style="FONT-SIZE: 14px; LINE-HEIGHT: 25px"><STRONG>作者Blog：</STRONG><A id=ArticleContent1_ArticleContent1_AuthorBlogLink href="http://blog.csdn.net/lithe/" target=_blank>http://blog.csdn.net/lithe/</A></DIV>
<DIV style="FONT-SIZE: 14px; COLOR: #900; LINE-HEIGHT: 25px"><STRONG>相关文章</STRONG></DIV>
<TABLE id=ArticleContent1_ArticleContent1_RelatedArticles cellSpacing=0 border=0>
<TBODY>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78117.shtm">C++字符串完全指引之二 —— 字符串封装类</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78116.shtm">C++字符串完全指引之一 —— Win32 字符编码</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78100.shtm">VC++中进程与多进程管理的方法</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/77/77060.shtm">解析#pragma指令</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/64/64798.shtm">用ATL建立轻量级的COM对象（一）</A> </TD></TR></TBODY></TABLE><img src ="http://www.cppblog.com/ivenher/aggbug/773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-10-21 14:27 <a href="http://www.cppblog.com/ivenher/articles/773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++字符串完全指引之二 —— 字符串封装类（转）</title><link>http://www.cppblog.com/ivenher/articles/772.html</link><dc:creator>爱饭盒</dc:creator><author>爱饭盒</author><pubDate>Fri, 21 Oct 2005 06:25:00 GMT</pubDate><guid>http://www.cppblog.com/ivenher/articles/772.html</guid><wfw:comment>http://www.cppblog.com/ivenher/comments/772.html</wfw:comment><comments>http://www.cppblog.com/ivenher/articles/772.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivenher/comments/commentRss/772.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivenher/services/trackbacks/772.html</trackback:ping><description><![CDATA[<TABLE cellPadding=4 width="100%" border=0>
<TBODY>
<TR>
<TD height=19></TD></TR>
<TR>
<TD width=10></TD>
<TD><SPAN id=ArticleContent1_ArticleContent1_lblContent>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<P align=center><B>C++字符串完全指引之二 —— 字符串封装类<BR></B><BR><BR>原著：Michael Dunn<BR><BR>作者：<A href="mailto:cjsun@insun.hit.edu.cn"><FONT color=#4a664d>Chengjie Sun</FONT></A></P>
<P><BR>原文出处：<A href="http://www.codeproject.com/string/cppstringguide2.asp" target=_blank><FONT color=#4a664d>CodeProject：The Complete Guide to C++ Strings, Part II</FONT></A><BR><BR><BR><FONT color=#4a664d><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"></FONT> <B>引言</B><BR><BR>　　因为C语言风格的字符串容易出错且不易管理，黑客们甚至利用可能存在的缓冲区溢出bug把C语言风格的字符串作为攻击目标，所以出现了很多字符串封装类。不幸的是，在某些场合下我们不知道该使用哪个字符串类，也不知道怎样把一个C风格的字符串转换成一个字符串封装类。<BR>　　这篇文章将介绍所有在Win32 API, MFC, STL, WTL 和 Visual C++ 运行库中出现的字符串类型。我将描述每一个类的用法，告诉大家怎样创建每一个类的对象以及怎样把一个类转换成其他类。受控字符串和Visual C++ 7中的类两部分是Nish完成的。<BR>　　为了更好的从这篇文章中受益，你必须要明白不同的字符类型和编码，这些内容我在<FONT color=#4a664d>第一部分</FONT>中介绍过。</P><PRE>Rule #1 of string classes</PRE>
<P>　　使用cast来实现类型转换是不好的做法，除非有文档明确指出这种转换可以使用。<BR>促使我写这两篇文章的原因是字符串类型转换中经常遇到的一些问题。当我们使用cast把字符串从类型X转换到类型Z的时候，我们不知道为什么代码不能正常工作。各种各样的字符串类型，尤其是BSTR，几乎没有在任何一个地方的文档中被明确的指出可以用cast来实现类型转换。所以我想一些人可能会使用cast来实现类型转换并希望这种转换能够正常工作。<BR>　　除非源字符串是一个被明确指明支持转换操作符的字符串包装类，否则cast不对字符串做任何转换。对常量字符串使用cast不会起到任何作用，所以下面的代码： </P><PRE>void SomeFunc ( LPCWSTR widestr );
main()
{
  SomeFunc ( (LPCWSTR) "C:\\foo.txt" );  // WRONG!
}      </PRE>　　肯定会失败。它可以被编译，因为cast操作会撤消编译器的类型检查。但是，编译可以通过并不能说明代码是正确的。<BR>　　在下面的例子中，我将会指明cast在什么时候使用是合法的。<PRE>C-style strings and typedefs</PRE>
<P>　　正如我在第一部分中提到的，windows APIs 是用TCHARs来定义的，在编译时，它可以根据你是否定义_MBCS或者_UNICODE被编译成MBCS或者Unicode字符。你可以参看第一部分中对TCHAR的完整描述，这里为了方便，我列出了字符的typedefs</P>
<TABLE id=AutoNumber1 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=4 cellPadding=4 width="90%" bgColor=#ffffff border=1>
<TBODY>
<TR>
<TD align=middle width="19%"><B>Type</B></TD>
<TD align=middle width="81%"><B>Meaning</B></TD></TR>
<TR>
<TD align=middle width="19%">WCHAR</TD>
<TD align=middle width="81%">Unicode character (wchar_t)</TD></TR>
<TR>
<TD align=middle width="19%">TCHAR</TD>
<TD align=middle width="81%">MBCS or Unicode character, depending on preprocessor settings</TD></TR>
<TR>
<TD align=middle width="19%">LPSTR</TD>
<TD align=middle width="81%">&nbsp;string of char (char*)</TD></TR>
<TR>
<TD align=middle width="19%">LPCSTR</TD>
<TD align=middle width="81%">constant string of char (const char*)</TD></TR>
<TR>
<TD align=middle width="19%">LPWSTR</TD>
<TD align=middle width="81%">&nbsp;string of WCHAR (WCHAR*)</TD></TR>
<TR>
<TD align=middle width="19%">LPCWSTR</TD>
<TD align=middle width="81%">&nbsp;constant string of WCHAR (const WCHAR*)</TD></TR>
<TR>
<TD align=middle width="19%">LPTSTR</TD>
<TD align=middle width="81%">&nbsp;string of TCHAR (TCHAR*)</TD></TR>
<TR>
<TD align=middle width="19%">LPCTSTR</TD>
<TD align=middle width="81%">&nbsp;constant string of TCHAR (const TCHAR*)</TD></TR></TBODY></TABLE>
<P>　　一个增加的字符类型是OLETYPE。它表示自动化接口（如word提供的可以使你操作文档的接口）中使用的字符类型。这种类型一般被定义成wchar_t，然而如果你定义了OLE2ANSI预处理标记，OLECHAR将会被定义成char类型。我知道现在已经没有理由定义OLE2ANSI（从MFC3以后，微软已经不使用它了），所以从现在起我将把OLECHAR当作Unicode字符。<BR>这里给出你将会看到的一些OLECHAR相关的typedefs：</P>
<TABLE id=AutoNumber2 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=3 cellPadding=3 width="71%" bgColor=#ffffff border=1>
<TBODY>
<TR>
<TD align=middle width="18%"><B>Type</B></TD>
<TD align=middle width="82%"><B>Meaning</B></TD></TR>
<TR>
<TD align=middle width="18%">OLECHAR </TD>
<TD align=middle width="82%">Unicode character (wchar_t)</TD></TR>
<TR>
<TD align=middle width="18%">LPOLESTR</TD>
<TD align=middle width="82%">&nbsp;string of OLECHAR (OLECHAR*)</TD></TR>
<TR>
<TD align=middle width="18%">LPCOLESTR </TD>
<TD align=middle width="82%">constant string of OLECHAR (const OLECHAR*)</TD></TR></TBODY></TABLE>
<P>　　还有两个用于包围字符串和字符常量的宏定义，它们可以使同样的代码被用于MBCS和Unicode builds ：</P>
<TABLE id=AutoNumber3 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=3 cellPadding=3 width="88%" bgColor=#ffffff border=1>
<TBODY>
<TR>
<TD align=middle width="14%"><B>Type </B></TD>
<TD align=middle width="86%"><B>Meaning</B></TD></TR>
<TR>
<TD align=middle width="14%">_T(x)</TD>
<TD align=middle width="86%">Prepends L to the literal in Unicode builds.</TD></TR>
<TR>
<TD align=middle width="14%">OLESTR(x)</TD>
<TD align=middle width="86%">Prepends L to the literal to make it an LPCOLESTR. </TD></TR></TBODY></TABLE>
<P>　　在文档或例程中，你还会看到好多_T的变体。有四个等价的宏定义，它们是TEXT, _TEXT, __TEXT和__T，它们都起同样的做用。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>COM 中的字符串 —— BSTR 和 VARIANT</B><BR><BR>　　很多自动化和COM接口使用BSTR来定义字符串。BSTRs中有几个"陷阱"，所以这里我用单独的部分来说明它。<BR>　　BSTR 是 Pascal-style 字符串（字符串长度被明确指出）和C-style字符串（字符串的长度要通过寻找结束符来计算）的混合产物。一个BSTR是一个Unicode字符串，它的长度是预先考虑的，并且它还有一个0字符作为结束标记。下面是一个BSTR的示例：<BR><BR>　</P>
<TABLE id=AutoNumber4 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=3 cellPadding=3 width="73%" bgColor=#ffffff border=1>
<TBODY>
<TR>
<TD align=middle width="20%">06 00 00 00</TD>
<TD align=middle width="20%">42 00</TD>
<TD align=middle width="20%">6F 00</TD>
<TD align=middle width="20%">62 00</TD>
<TD align=middle width="20%">00 00</TD></TR>
<TR>
<TD align=middle width="20%">--length--</TD>
<TD align=middle width="20%">B</TD>
<TD align=middle width="20%">o</TD>
<TD align=middle width="20%">b</TD>
<TD align=middle width="20%">EOS</TD></TR></TBODY></TABLE>
<P>　　注意字符串的长度是如何被加到字符串数据中的。长度是DWORD类型的，保存了字符串中包含的字节数，但不包括结束标记。在这个例子中，"Bob"包含3个Unicode字符（不包括结束符），总共6个字节。字符串的长度被预先存储好，以便当一个BSTR在进程或者计算机之间被传递时，COM库知道多少数据需要传送。（另一方面，一个BSTR能够存储任意数据块，而不仅仅是字符，它还可以包含嵌入在数据中的0字符。然而，由于这篇文章的目的，我将不考虑那些情况）。<BR>　　在 C++ 中，一个 BSTR 实际上就是一个指向字符串中第一个字符的指针。它的定义如下： </P><PRE>BSTR bstr = NULL;
  bstr = SysAllocString ( L"Hi Bob!" ); 
  if ( NULL == bstr )
    // out of memory error 
  // Use bstr here...
 SysFreeString ( bstr );      </PRE>自然的，各种各样的BSTR封装类为你实现内存管理。<BR>　　另外一个用在自动化接口中的变量类型是VARIANT。它被用来在无类型（typeless）语言，如Jscript和VBScript，来传递数据。一个VARIANT可能含有很多不同类型的数据，例如long和IDispatch*。当一个VARIANT包含一个字符串，字符串被存成一个BSTR。当我后面讲到VARIANT封装类时，我会对VARIANT多些介绍。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>字符串封装类</B><BR><BR>　　到目前为止，我已经介绍了各种各样的字符串。下面，我将说明封装类。对于每个封装类，我将展示怎样创建一个对象及怎样把它转换成一个C语言风格的字符串指针。C语言风格的字符串指针对于API的调用，或者创建一个不同的字符串类对象经常是必需的。我不会介绍字符串类提供的其他操作，比如排序和比较。<BR>　　重复一遍，除非你确切的明白结果代码将会做什么，否则不要盲目地使用cast来实现类型转换。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>CRT提供的类</B><BR><BR><B><I>_bstr_t</I></B><BR>　　_bstr_t是一个对BSTR的完整封装类，实际上它隐藏了底层的BSTR。它提供各种构造函数和操作符来访问底层的C语言风格的字符串。然而，_bstr_t却没有访问BSTR本身的操作符，所以一个_bstr_t类型的字符串不能被作为输出参数传给一个COM方法。如果你需要一个BSTR*参数，使用ATL类CComBSTR是比较容易的方式。<BR>　　一个_bstr_t字符串能够传给一个接收参数类型为BSTR的函数，只是因为下列3个条件同时满足。首先，_bstr_t有一个向wchar_t*转换的转换函数；其次，对编译器而言，因为BSTR的定义，wchar_t*和BSTR有同样的含义；第三，_bstr_t内部含有的wchar_t*指向一片按BSTR的形式存储数据的内存。所以，即使没有文档说明，_bstr_t可以转换成BSTR，这种转换仍然可以正常进行。 <PRE>// Constructing
_bstr_t bs1 = "char string";       // construct from a LPCSTR
_bstr_t bs2 = L"wide char string"; // construct from a LPCWSTR
_bstr_t bs3 = bs1;                 // copy from another _bstr_t
_variant_t v = "Bob";
_bstr_t bs4 = v;                   // construct from a _variant_t that has a string
 
// Extracting data
LPCSTR psz1 = bs1;              // automatically converts to MBCS string
LPCSTR psz2 = (LPCSTR) bs1;     // cast OK, same as previous line
LPCWSTR pwsz1 = bs1;            // returns the internal Unicode string
LPCWSTR pwsz2 = (LPCWSTR) bs1;  // cast OK, same as previous line
BSTR    bstr = bs1.copy();      // copies bs1, returns it as a BSTR
 
  // ...
SysFreeString ( bstr );      </PRE>　　注意_bstr_t也提供char*和wchar_t*之间的转换操作符。这是一个值得怀疑的设计，因为即使它们是非常量字符串指针，你也一定不能使用这些指针去修改它们指向的缓冲区的内容，因为那将破坏内部的BSTR结构。<BR><BR><B><I>_variant_t</I></B><BR>　　_variant_t是一个对VARIANT的完整封装，它提供很多构造函数和转换函数来操作一个VARIANT可能包含的大量的数据类型。这里，我将只介绍与字符串有关的操作。 <PRE>// Constructing
_variant_t v1 = "char string";       // construct from a LPCSTR
_variant_t v2 = L"wide char string"; // construct from a LPCWSTR
_bstr_t bs1 = "Bob";
_variant_t v3 = bs1;                 // copy from a _bstr_t object
 
// Extracting data
_bstr_t bs2 = v1;           // extract BSTR from the VARIANT
_bstr_t bs3 = (_bstr_t) v1; // cast OK, same as previous line      </PRE><B>注意</B>：<BR>　　如果类型转换不能被执行，_variant_t方法能够抛出异常，所以应该准备捕获_com_error异常。<BR><BR><B>还需要注意的是</B>：<BR>　　没有从一个_variant_t变量到一个MBCS字符串的直接转换。你需要创建一个临时的_bstr_t变量，使用提供Unicode到MBCS转换的另一个字符串类或者使用一个ATL转换宏。<BR>　　不像_bstr_t，一个_variant_t变量可以被直接作为参数传递给一个COM方法。_variant_t<BR>　　继承自VARIANT类型，所以传递一个_variant_t来代替VARIANT变量是C++语言所允许的。<BR><BR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>STL 类</B><BR>　　STL只有一个字符串类，basic_string。一个basic_string管理一个以0做结束符的字符串数组。字符的类型是basic_string模般的参数。总的来说，一个basic_string类型的变量应该被当作不透明的对象。你可以得到一个指向内部缓冲区的只读指针，但是任何写操作必须使用basic_string的操作符和方法。<BR>　　basic_string有两个预定义的类型：包含char的string类型和包含wchar_t的wstring类型。这里没有内置的包含TCHAR的类型，但是你可以使用下面列出的代码来实现。 <PRE>// Specializations
typedef basic_string<TCHAR></TCHAR> tstring; // string of TCHARs
 
// Constructing
string str = "char string";         // construct from a LPCSTR
wstring wstr = L"wide char string"; // construct from a LPCWSTR
tstring tstr = _T("TCHAR string");  // construct from a LPCTSTR
 
// Extracting data
LPCSTR psz = str.c_str();    // read-only pointer to str''s buffer
LPCWSTR pwsz = wstr.c_str(); // read-only pointer to wstr''s buffer
LPCTSTR ptsz = tstr.c_str(); // read-only pointer to tstr''s buffer
</PRE>　　不像_bstr_t，一个basic_string变量不能在字符集之间直接转换。然而，你可以传递由c_str()返回的指针给另外一个类的构造函数（如果这个类的构造函数接受这种字符类型）。例如： <PRE>// Example, construct _bstr_t from basic_string
_bstr_t bs1 = str.c_str();  // construct a _bstr_t from a LPCSTR
_bstr_t bs2 = wstr.c_str(); // construct a _bstr_t from a LPCWSTR      </PRE><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>ATL 类</B><BR><BR><B><I>CComBSTR</I></B><BR>　　CComBSTR 是 ATL 中的 BSTR 封装类，它在某些情况下比_bstr_t有用的多。最引人注意的是CComBSTR允许访问底层的BSTR，这意味着你可以传递一个CComBSTR对象给COM的方法。CComBSTR对象能够替你自动的管理BSTR的内存。例如，假设你想调用下面这个接口的方法： <PRE>// Sample interface:
struct IStuff : public IUnknown
{
  // Boilerplate COM stuff omitted...
  STDMETHOD(SetText)(BSTR bsText);
  STDMETHOD(GetText)(BSTR* pbsText);
};      </PRE>　　CComBSTR有一个操作符--BSTR方法，所以它能直接被传给SetText()函数。还有另外一个操作--&amp;，这个操作符返回一个BSTR*。所以，你可以对一个CComBSTR对象使用&amp;操作符，然后把它传给需要BSTR*参数的函数。 <PRE>CComBSTR bs1;
CComBSTR bs2 = "new text";
 
  pStuff-&gt;GetText ( &amp;bs1 );       // ok, takes address of internal BSTR
  pStuff-&gt;SetText ( bs2 );        // ok, calls BSTR converter
  pStuff-&gt;SetText ( (BSTR) bs2 ); // cast ok, same as previous line      </PRE>　　CComBSTR有和_bstr_t相似的构造函数，然而却没有内置的向MBCS字符串转换的函数。因此，你需要使用一个ATL转换宏。 <PRE>// Constructing
CComBSTR bs1 = "char string";       // construct from a LPCSTR
CComBSTR bs2 = L"wide char string"; // construct from a LPCWSTR
CComBSTR bs3 = bs1;                 // copy from another CComBSTR
CComBSTR bs4;

  bs4.LoadString ( IDS_SOME_STR );  // load string from string table
// Extracting data
BSTR bstr1 = bs1;        // returns internal BSTR, but don''t modify it!
BSTR bstr2 = (BSTR) bs1; // cast ok, same as previous line
BSTR bstr3 = bs1.Copy(); // copies bs1, returns it as a BSTR
BSTR bstr4;
  bstr4 = bs1.Detach();  // bs1 no longer manages its BSTR
  // ...
  SysFreeString ( bstr3 );
  SysFreeString ( bstr4 );      </PRE>　　注意在上个例子中使用了Detach()方法。调用这个方法后，CComBSTR对象不再管理它的BSTR字符串或者说它对应的内存。这就是bstr4需要调用SysFreeString()的原因。<BR>　　做一个补充说明：重载的&amp;操作符意味着在一些STL容器中你不能直接使用CComBSTR变量，比如list。容器要求&amp;操作符返回一个指向容器包含的类的指针，但是对CComBSTR变量使用&amp;操作符返回的是BSTR*，而不是CComBSTR*。然而，有一个ATL类可以解决这个问题，这个类是CAdapt。例如，你可以这样声明一个CComBSTR的list：<PRE>std::list&lt; CAdapt<CCOMBSTR></CCOMBSTR> &gt; bstr_list;</PRE>
<P>　　CAdapt提供容器所需要的操作符，但这些操作符对你的代码是透明的。你可以把一个bstr_list当作一个CComBSTR的list来使用。<BR><BR><B><I>CComVariant</I></B><BR>　　CComVariant是VARIANT的封装类。然而，不像_variant_t，在CComVariant中VARIANT没有被隐藏。事实上你需要直接访问VARIANT的成员。CComVariant提供了很多构造函数来对VARIANT能够包含的多种类型进行处理。这里，我将只介绍和字符串相关的操作。 </P><PRE>// Constructing
CComVariant v1 = "char string";       // construct from a LPCSTR
CComVariant v2 = L"wide char string"; // construct from a LPCWSTR
CComBSTR bs1 = "BSTR bob";
CComVariant v3 = (BSTR) bs1;          // copy from a BSTR
 
// Extracting data
CComBSTR bs2 = v1.bstrVal;            // extract BSTR from the VARIANT      </PRE>　　不像_variant_t，这里没有提供针对VARIANT包含的各种类型的转换操作符。正如上面介绍的，你必须直接访问VARIANT的成员并且确保这个VARIANT变量保存着你期望的类型。如果你需要把一个CComVariant类型的数据转换成一个BSTR类型的数据，你可以调用ChangeType()方法。 <PRE>CComVariant v4 = ... // Init v4 from somewhere
CComBSTR bs3;
 
  if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
    bs3 = v4.bstrVal;      </PRE>　　像_variant_t一样，CComVariant也没有提供向MBCS字符串转换的转换操作。你需要创建一个_bstr_t类型的中间变量，使用提供从Unicode到MBCS转换的另一个字符串类，或者使用一个ATL的转换宏。<BR><BR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>ATL转换宏</B><BR><BR>　　ATL：转换宏是各种字符编码之间进行转换的一种很方便的方式，在函数调用时，它们显得非常有用。ATL转换宏的名称是根据下面的模式来命名的[源类型]2[新类型]或者[源类型]2C[新类型]。据有第二种形式的名字的宏的转换结果是常量指针（对应名字中的"C"）。各种类型的简称如下：<PRE>A: MBCS string, char* (A for ANSI)
W: Unicode string, wchar_t* (W for wide)
T: TCHAR string, TCHAR*
OLE: OLECHAR string, OLECHAR* (in practice, equivalent to W)
BSTR: BSTR (used as the destination type only)</PRE>
<P>　　所以，W2A()宏把一个Unicode字符串转换成一个MBCS字符串。T2CW()宏把一个TCHAR字符串转转成一个Unicode字符串常量。<BR>　　为了使用这些宏，需要先包含atlconv.h头文件。你甚至可以在非ATL工程中包含这个头文件来使用其中定义的宏，因为这个头文件独立于ATL中的其他部分，不需要一个_Module全局变量。当你在一个函数中使用转换宏时，需要把USES_CONVERSION宏放在函数的开头。它定义了转换宏所需的一些局部变量。<BR>　　当转换的目的类型是除了BSTR以外的其他类型时，被转换的字符串是存在栈中的。所以，如果你想让字符串的生命周期比当前的函数长，你需要把这个字符串拷贝到其他的字符串类中。当目的类型是BSTR时，内存不会自动被释放，你必须把返回值赋给一个BSTR变量或者一个BSTR封装类以避免内存泄漏。<BR>　　下面是一些各种转换宏的使用例子： </P><PRE>// Functions taking various strings:
void Foo ( LPCWSTR wstr );
void Bar ( BSTR bstr );
// Functions returning strings:
void Baz ( BSTR* pbstr );
#include <ATLCONV.H></ATLCONV.H>
main()
{
using std::string;
USES_CONVERSION;    // declare locals used by the ATL macros
// Example 1: Send an MBCS string to Foo()
LPCSTR psz1 = "Bob";
string str1 = "Bob";
 
  Foo ( A2CW(psz1) );
  Foo ( A2CW(str1.c_str()) );
 
// Example 2: Send a MBCS and Unicode string to Bar()
LPCSTR psz2 = "Bob";
LPCWSTR wsz = L"Bob";
BSTR bs1;
CComBSTR bs2;
 
  bs1 = A2BSTR(psz2);         // create a BSTR
  bs2.Attach ( W2BSTR(wsz) ); // ditto, assign to a CComBSTR 
  Bar ( bs1 );
  Bar ( bs2 );
 
  SysFreeString ( bs1 );      // free bs1 memory
  // No need to free bs2 since CComBSTR will do it for us.
 
// Example 3: Convert the BSTR returned by Baz()
BSTR bs3 = NULL;
string str2;
  Baz ( &amp;bs3 );          // Baz() fills in bs3
  str2 = W2CA(bs3);      // convert to an MBCS string
  SysFreeString ( bs3 ); // free bs3 memory
}      </PRE>　　正如你所看见的，当你有一个和函数所需的参数类型不同的字符串时，使用这些转换宏是非常方便的。 <BR><BR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>MFC类</B><BR><BR><B><I>CString</I></B><BR>　　因为一个MFC CString类的对象包含TCHAR类型的字符，所以确切的字符类型取决于你所定义的预处理符号。大体来说，CString 很像STL string，这意味着你必须把它当成不透明的对象，只能使用CString提供的方法来修改CString对象。CString有一个string所不具备的优点：CString具有接收MBCS和Unicode两种字符串的构造函数，它还有一个LPCTSTR转换符，所以你可以把CString对象直接传给一个接收LPCTSTR的函数而不需要调用c_str()函数。 <PRE>// Constructing
CString s1 = "char string";  // construct from a LPCSTR
CString s2 = L"wide char string";  // construct from a LPCWSTR
CString s3 ( '' '', 100 );  // pre-allocate a 100-byte buffer, fill with spaces
CString s4 = "New window text";
 
  // You can pass a CString in place of an LPCTSTR:
  SetWindowText ( hwndSomeWindow, s4 );
 
  // Or, equivalently, explicitly cast the CString:
  SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );        </PRE>　　你可以从你的字符串表中装载一个字符串，CString的一个构造函数和LoadString()函数可以完成它。Format()方法能够从字符串表中随意的读取一个具有一定格式的字符串。　　　　　 <PRE>// Constructing/loading from string table
CString s5 ( (LPCTSTR) IDS_SOME_STR );  // load from string table
CString s6, s7; 
  // Load from string table.
  s6.LoadString ( IDS_SOME_STR );
 
  // Load printf-style format string from the string table:
  s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );  </PRE>　　第一个构造函数看起来有点奇怪，但是这实际上是文档说明的装入一个字符串的方法。 注意，对一个CString变量，你可以使用的唯一合法转换符是LPCTSTR。转换成LPTSTR（非常量指针）是错误的。养成把一个CString变量转换成LPTSTR的习惯将会给你带来伤害，因为当你的程序后来崩溃时，你可能不知道为什么，因为你到处都使用同样的代码而那时它们都恰巧正常工作。正确的得到一个指向缓冲区的非常量指针的方法是调用GetBuffer()方法。下面是正确的用法的一个例子，这段代码是给一个列表控件中的项设定文字： <PRE>CString str = _T("new text");
LVITEM item = {0};
  item.mask = LVIF_TEXT;
  item.iItem = 1;
  item.pszText = (LPTSTR)(LPCTSTR) str; // WRONG!
  item.pszText = str.GetBuffer(0);      // correct
 
  ListView_SetItem ( &amp;item );
str.ReleaseBuffer();  // return control of the buffer to str      </PRE>　　pszText成员是一个LPTSTR变量，一个非常量指针，因此你需要对str调用GetBuffer()。GetBuffer()的参数是你需要CString为缓冲区分配的最小长度。如果因为某些原因，你需要一个可修改的缓冲区来存放1K TCHARs，你需要调用GetBuffer(1024)。把0作为参数时，GetBuffer()返回的是指向字符串当前内容的指针。<BR>　　上面划线的语句可以被编译，在这种情况下，甚至可以正常起作用。但这并不意味着这行代码是正确的。通过使用非常量转换，你已经破坏了面向对象的封装，并对CString的内部实现作了某些假定。如果你有这样的转换习惯，你终将会陷入代码崩溃的境地。你会想代码为什么不能正常工作了，因为你到处都使用同样的代码而那些代码看起来是正确的。<BR>　　你知道人们总是抱怨现在的软件的bug是多么的多吗？软件中的bug是因为程序员写了不正确的代码。难道你真的想写一些你知道是错误的代码来为所有的软件都满是bug这种认识做贡献吗？花些时间来学习使用CString的正确方法让你的代码在任何时间都正常工作把。<BR>　　CString 有两个函数来从一个 CString 创建一个 BSTR。它们是 AllocSysString() 和SetSysString()。 <PRE>// Converting to BSTR
CString s5 = "Bob!";
BSTR bs1 = NULL, bs2 = NULL;
  bs1 = s5.AllocSysString();
  s5.SetSysString ( &amp;bs2 );
  SysFreeString ( bs1 );
  SysFreeString ( bs2 );      </PRE><B><I>COleVariant</I></B><BR>　　COleVariant和CComVariant.很相似。COleVariant继承自VARIANT，所以它可以传给接收VARIANT的函数。然而，不像CComVariant，COleVariant只有一个LPCTSTR构造函数。没有对LPCSTR 和LPCWSTR的构造函数。在大多数情况下这不是一个问题，因为不管怎样你的字符串很可能是LPCTSTRs，但这是一个需要意识到的问题。COleVariant还有一个接收CString参数的构造函数。 <PRE>// Constructing
CString s1 = _T("tchar string");
COleVariant v1 = _T("Bob"); // construct from an LPCTSTR
COleVariant v2 = s1; // copy from a CString      </PRE>　　像CComVariant一样，你必须直接访问VARIANT的成员。如果需要把VARIANT转换成一个字符串，你应该使用ChangeType()方法。然而，COleVariant::ChangeType()如果失败会抛出异常，而不是返回一个表示失败的HRESULT代码。 <PRE>// Extracting data
COleVariant v3 = ...; // fill in v3 from somewhere
BSTR bs = NULL;
  try
    {
    v3.ChangeType ( VT_BSTR );
    bs = v3.bstrVal;
    }
  catch ( COleException* e )
    {
    // error, couldn''t convert
    }
  SysFreeString ( bs );      </PRE><BR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>WTL 类</B><BR><BR><B><I>CString</I></B><BR>　　WTL的CString的行为和MFC的 CString完全一样，所以你可以参考上面关于MFC的 CString的介绍。<BR><BR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>CLR 和 VC 7 类</B><BR><BR>　　System::String是用来处理字符串的.NET类。在内部，一个String对象包含一个不可改变的字符串序列。任何对String对象的操作实际上都是返回了一个新的String对象，因为原始的对象是不可改变的。String的一个特性是如果你有不止一个String对象包含相同的字符序列，它们实际上是指向相同的对象的。相对于C++的使用扩展是增加了一个新的字符串常量前缀S，S用来代表一个受控的字符串常量（a managed string literal）。 <PRE>// Constructing
String* ms = S"This is a nice managed string";      </PRE>　　你可以传递一个非受控的字符串来创建一个String对象，但是样会比使用受控字符串来创建String对象造成效率的微小损失。这是因为所有以S作为前缀的相同的字符串实例都代表同样的对象，但这对非受控对象是不适用的。下面的代码清楚地阐明了这一点： <PRE>String* ms1 = S"this is nice";
String* ms2 = S"this is nice";
String* ms3 = L"this is nice";
  Console::WriteLine ( ms1 == ms2 ); // prints true
  Console::WriteLine ( ms1 == ms3);  // prints false      </PRE>正确的比较可能没有使用S前缀的字符串的方法是使用String::CompareTo() <PRE>  Console::WriteLine ( ms1-&gt;CompareTo(ms2) );
  Console::WriteLine ( ms1-&gt;CompareTo(ms3) );      </PRE>　　上面的两行代码都会打印0，0表示两个字符串相等。 String和MFC 7 CString之间的转换是很容易的。CString有一个向LPCTSTR的转换操作，而String有两个接收char* 和 wchar_t*的构造函数，因此你可以把一个CString变量直接传给一个String的构造函数。 <PRE>CString s1 ( "hello world" );
String* s2 ( s1 );  // copy from a CString      </PRE>反方向的转换也很类似 <PRE>String* s1 = S"Three cats";
CString s2 ( s1 );      </PRE>　　这也许会使你感到一点迷惑，但是它确实是起作用的。因为从VS.NET 开始，CString 有了一个接收String 对象的构造函数。 <PRE>  CStringT ( System::String* pString );      </PRE>对于一些快速操作，你可能想访问底层的字符串： <PRE>String* s1 = S"Three cats";
  Console::WriteLine ( s1 );
const __wchar_t __pin* pstr = PtrToStringChars(s1);
  for ( int i = 0; i &lt; wcslen(pstr); i++ )
    (*const_cast&lt;__wchar_t*&gt;(pstr+i))++;
  Console::WriteLine ( s1 );      </PRE>　　PtrToStringChars()返回一个指向底层字符串的const __wchar_t* ，我们需要固定它，否则垃圾收集器或许会在我们正在管理它的内容的时候移动了它。 <BR><BR><TCHAR></TCHAR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>在 printf-style 格式函数中使用字符串类</B><BR><BR>　　当你在printf()或者类似的函数中使用字符串封装类时你必须十分小心。这些函数包括sprintf()和它的变体，还有TRACE和ATLTRACE宏。因为这些函数没有对添加的参数的类型检查，你必须小心，只能传给它们C语言风格的字符串指针，而不是一个完整的字符串类。<BR>　　例如，要把一个_bstr_t 字符串传给ATLTRACE()，你必须使用显式转换(LPCSTR) 或者(LPCWSTR)：<PRE>_bstr_t bs = L"Bob!";
ATLTRACE("The string is: %s in line %d\n", (LPCSTR) bs, nLine);</PRE>
<P>　　如果你忘了使用转换符而把整个_bstr_t对象传给了函数，将会显示一些毫无意义的输出，因为_bstr_t保存的内部数据会全部被输出。<BR><BR><TCHAR></TCHAR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>所有类的总结</B><BR><BR>　　两个字符串类之间进行转换的常用方式是：先把源字符串转换成一个C语言风格的字符串指针，然后把这个指针传递给目的类型的构造函数。下面这张表显示了怎样把一个字符串转换成一个C语言风格的字符串指针以及哪些类具有接收C语言风格的字符串指针的构造函数。</P>
<TABLE id=AutoNumber5 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="100%" bgColor=#ffffff border=1>
<TBODY>
<TR>
<TD align=middle width="7%"><B><FONT size=1>Class&nbsp; </FONT></B></TD>
<TD align=middle width="10%"><TCHAR></TCHAR><B><FONT size=1>string type </FONT></B></TD>
<TD align=middle width="14%"><TCHAR></TCHAR><B><FONT size=1>convert to char*? </FONT></B></TD>
<TD align=middle width="12%"><TCHAR></TCHAR><B><FONT size=1>convert to const char*? </FONT></B></TD>
<TD align=middle width="12%"><TCHAR></TCHAR><B><FONT size=1>convert to wchar_t*? </FONT></B></TD>
<TD align=middle width="11%"><TCHAR></TCHAR><B><FONT size=1>convert to const wchar_t*? </FONT></B></TD>
<TD align=middle width="11%"><TCHAR></TCHAR><B><FONT size=1>convert to BSTR? </FONT></B></TD>
<TD align=middle width="11%"><TCHAR></TCHAR><B><FONT size=1>construct from char*? </FONT></B></TD>
<TD align=middle width="12%"><TCHAR></TCHAR><B><FONT size=1>construct from wchar_t*?</FONT></B></TD></TR>
<TR>
<TD width="7%">_bstr_t</TD>
<TD width="10%">BSTR</TD>
<TD width="14%">yes cast<SUP>1</SUP></TD>
<TD width="12%">yes cast</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes cast<TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><SUP>1</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>yes cast</TD>
<TD width="11%">yes<SUP>2</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">_variant_t</TD>
<TD width="10%">BSTR</TD>
<TD width="14%">no</TD>
<TD width="12%">no</TD>
<TD width="12%">no</TD>
<TD width="11%">cast to<BR>_bstr_t<SUP>3</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>cast to<BR>_bstr_t<SUP>3</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">string</TD>
<TD width="10%">MBCS</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%">yes c_str() method</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="11%">no</TD>
<TD width="11%">no</TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%">no</TD></TR>
<TR>
<TD width="7%">wstring</TD>
<TD width="10%">Unicode</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes c_str() method</TD>
<TD width="11%">no</TD>
<TD width="11%">no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">CComBSTR</TD>
<TD width="10%">BSTR</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="11%">yes cast to BSTR</TD>
<TD width="11%">yes cast</TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">CComVariant</TD>
<TD width="10%"><TCHAR></TCHAR><TCHAR></TCHAR>BSTR</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="11%">yes<SUP>4</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes<TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><SUP>4</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">CString </TD>
<TD width="10%">TCHAR</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no<SUP>6</SUP></TD>
<TD width="12%">in MBCS<BR>builds, cast</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no<TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><SUP>6</SUP></TD>
<TD width="11%">in Unicode<BR>builds, cast</TD>
<TD width="11%">no<SUP>5</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>yes</TD></TR>
<TR>
<TD width="7%">COleVariant</TD>
<TD width="10%"><TCHAR></TCHAR><TCHAR></TCHAR>BSTR</TD>
<TD width="14%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR>no</TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes<TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><SUP>4</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>yes<TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><SUP>4</SUP></TD>
<TD width="11%"><TCHAR></TCHAR><TCHAR></TCHAR>in MBCS<BR>builds</TD>
<TD width="12%"><TCHAR></TCHAR><TCHAR></TCHAR>in Unicode<BR>builds</TD></TR>
<TR>
<TD width="100%" colSpan=9>
<DIR>
<LI>1、即使 _bstr_t 提供了向非常量指针的转换操作符，修改底层的缓冲区也会已引起GPF如果你溢出了缓冲区或者造成内存泄漏。 
<LI>2<TCHAR></TCHAR><TCHAR></TCHAR>、_bstr_t 在内部用一个 wchar_t* 来保存 BSTR，所以你可以使用 const wchar_t* 来访问BSTR。这是一个实现细节，你可以小心的使用它，将来这个细节也许会改变。 
<LI>3<TCHAR></TCHAR><TCHAR></TCHAR>、如果数据不能转换成BSTR会抛出一个异常。 
<LI>4<TCHAR></TCHAR><TCHAR></TCHAR>、使用 ChangeType()，然后访问 VARIANT 的 bstrVal 成员。在MFC中，如果数据转换不成功将会抛出异常。 
<LI>5<TCHAR></TCHAR><TCHAR></TCHAR>、这里没有转换 BSTR 函数，然而 AllocSysString() 返回一个新的BSTR。 
<LI>6<TCHAR></TCHAR><TCHAR></TCHAR>、使用 GetBuffer() 方法，你可以暂时地得到一个非常量的TCHAR指针。 </LI></DIR></TD></TR></TBODY></TABLE>
<P><BR></P></TD></TR>
<TR>
<TD><TCHAR></TCHAR><TCHAR></TCHAR><TCHAR></TCHAR><IMG alt="" src="http://www.vckbase.com/document/image/paragraph.gif"> <B>作者简介<BR><BR></B><I>Michael Dunn：<BR>　　</I>Michael Dunn居住在阳光城市洛杉矶。他是如此的喜欢这里的天气以致于想一生都住在这里。他在4年级时开始编程，那时用的电脑是Apple //e。1995年，在UCLA获得数学学士学位，随后在Symantec公司做QA工程师，在 Norton AntiVirus 组工作。他自学了 Windows 和 MFC 编程。1999-2000年，他设计并实现了 Norton AntiVirus的新界面。<BR>　　Michael 现在在 Napster（一个提供在线订阅音乐服务的公司）做开发工作，他还开发了UltraBar，一个IE工具栏插件，它可以使网络搜索更加容易，给了 googlebar 以沉重打击；他还开发了 CodeProject SearchBar；与人共同创建了 Zabersoft 公司，该公司在洛杉矶和丹麦的 Odense 都设有办事处。<BR>　　他喜欢玩游戏。爱玩的游戏有 pinball, bike riding，偶尔还玩 PS, Dreamcasth 和 MAME 游戏。他因忘了自己曾经学过的语言：法语、汉语、日语而感到悲哀。<BR><BR><I>Nishant S(Nish)</I>：<BR>　　Nish是来自印度 Trivandrum,的 Microsoft Visual C++ MVP。他从1990年开始编码。现在，Nish为作为合同雇员在家里为 CodeProject 工作。　　　<BR>　　他还写了一部浪漫戏剧《<TCHAR></TCHAR><TCHAR></TCHAR>Summer Love and Some more Cricket<TCHAR></TCHAR><TCHAR></TCHAR>》和一本编程书籍《<TCHAR></TCHAR>Extending MFC applications with the .NET Framework<TCHAR></TCHAR>》。他还管理者MVP的一个网站http://www.voidnish.com/ 。在这个网站上，你可以看到他的很多关于编程方面的思想和文章。<BR>Nish 还计划好<TCHAR></TCHAR>了<TCHAR></TCHAR>旅游，他希望自一生中能够到达地球上尽可能多的地方。</TD></TR></TBODY></TABLE></SPAN><BR>
<DIV style="FONT-SIZE: 14px; LINE-HEIGHT: 25px"><STRONG>作者Blog：</STRONG><A id=ArticleContent1_ArticleContent1_AuthorBlogLink href="http://blog.csdn.net/lithe/" target=_blank>http://blog.csdn.net/lithe/</A></DIV>
<DIV style="FONT-SIZE: 14px; COLOR: #900; LINE-HEIGHT: 25px"><STRONG>相关文章</STRONG></DIV>
<TABLE id=ArticleContent1_ArticleContent1_RelatedArticles cellSpacing=0 border=0>
<TBODY>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78117.shtm">C++字符串完全指引之二 —— 字符串封装类</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78116.shtm">C++字符串完全指引之一 —— Win32 字符编码</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/78/78100.shtm">VC++中进程与多进程管理的方法</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/77/77060.shtm">解析#pragma指令</A> </TD></TR>
<TR>
<TD><A href="http://dev.csdn.net/article/64/64798.shtm">用ATL建立轻量级的COM对象（一）</A> </TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.cppblog.com/ivenher/aggbug/772.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivenher/" target="_blank">爱饭盒</a> 2005-10-21 14:25 <a href="http://www.cppblog.com/ivenher/articles/772.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>