﻿<?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>BlogJava-听风流过-文章分类-C++</title><link>http://www.blogjava.net/tingfeng/category/38486.html</link><description>在仰望，在远眺，在思索，在彷徨~</description><language>zh-cn</language><lastBuildDate>Tue, 11 Apr 2017 21:10:01 GMT</lastBuildDate><pubDate>Tue, 11 Apr 2017 21:10:01 GMT</pubDate><ttl>60</ttl><item><title>【转】Zlib库的安装与使用</title><link>http://www.blogjava.net/tingfeng/articles/432445.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Tue, 11 Apr 2017 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/432445.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/432445.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/432445.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/432445.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/432445.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在实际应用中经常会遇到要压缩数据的问题，常见的压缩格式有zip和rar,而Linux下那就更多了,bz2,gz,xz什么的都有，单单Linux下的解压和压缩命令就有好多呢？没有什么好不好的。查了资料，应该是zlib这个比较简单好用。应用也广，所以就准备用这个了。						　　下载Zlib库，地址: http://zlib.net/zlib128.zip 用wget下载，然后再...&nbsp;&nbsp;<a href='http://www.blogjava.net/tingfeng/articles/432445.html'>阅读全文</a><img src ="http://www.blogjava.net/tingfeng/aggbug/432445.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2017-04-11 16:30 <a href="http://www.blogjava.net/tingfeng/articles/432445.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Socket阻塞通信</title><link>http://www.blogjava.net/tingfeng/articles/411469.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Tue, 25 Mar 2014 14:45:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/411469.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/411469.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/411469.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/411469.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/411469.html</trackback:ping><description><![CDATA[server.cpp
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"WS2_32.lib")
void main()
{
    WSADATA wsd;
    SOCKET server;                                            //服务器socket
    SOCKADDR_IN addrSrv;    
    char sendBuf[100];
    char recvBuf[100];
    SOCKADDR_IN addrClient;
    SOCKET client;                                            //连接的客户端socket
    int len;
    if(WSAStartup(MAKEWORD(2,2),&amp;wsd)!=0)
    {
        printf("start up failed!\n");
        return ;
    }
    server=socket(AF_INET,SOCK_STREAM,0);                    //创建socket
    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);            //设置地址
    addrSrv.sin_family=AF_INET;
    addrSrv.sin_port=htons(6000);                            //设置端口号
    bind(server,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));        //绑定
    listen(server,5);                                        //设置最多连接数
    len=sizeof(SOCKADDR);
    while(1)
    {
        client=accept(server,(SOCKADDR*)&amp;addrClient,&amp;len);    //接收客户端连接
        sprintf(sendBuf,"Welcome %s ",
        inet_ntoa(addrClient.sin_addr));
        send(client,sendBuf,strlen(sendBuf)+1,0);            //发送信息客户端
        recv(client,recvBuf,100,0);                            //接收客户端数据
        printf("%s\n",recvBuf);
        closesocket(client);
    }
    closesocket(client);                                    //关闭连接
    WSACleanup();
}

client.cpp
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"WS2_32.lib")
void main()
{
   

	int call = 0;
	int result = 0;
	while(1)
	{
		printf("\n\n请输入命令：\n");
		printf("\t1:发起连接\n");
		printf("\t2:发送数据\n");
		printf("\t3:关闭连接\n");
		printf("\t4:重新连接\n");
		printf("\t0:关闭程序\n");
		scanf("%d",&amp;call);
		try{
			switch(call)
			{
			case 1:
				printf("发起连接\n");
				WSADATA wsd;
				SOCKET sockClient;                                            //客户端socket
				SOCKADDR_IN addrSrv;
				char recvBuf[100];
				char sendbuf[100];
				if(WSAStartup(MAKEWORD(2,2),&amp;wsd)!=0)
				{
					printf("start up failed!\n");
					return ;
				}
				sockClient=socket(AF_INET,SOCK_STREAM,0);                    //创建socket
				addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
				addrSrv.sin_family=AF_INET;
				addrSrv.sin_port=htons(6000);
				connect(sockClient,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));    //连接服务器端
				recv(sockClient,recvBuf,100,0);                                //接收服务器端数据
				printf("%s\n",recvBuf);
				break;
			case 2:
				printf("请输入要发送的文字\n");
				scanf("%s",&amp;sendbuf);
				printf("%s\n",sendbuf);
				result = send(sockClient,sendbuf,50,0);    //向服务器端发送数据
				printf("操作结果：%d\n",result);
				break;
			case 3:
				result = closesocket(sockClient); 
				printf("操作结果：%d\n",result);//关闭连接
				WSACleanup();
				printf("连接已经关闭\n");
				break;
			case 4:
				printf("重新连接\n");//此处功能暂未实现
				break;
			case 0:
				return;
			default:
				break;
			}
		}catch(Exception e)
		{
		}
	}    
}</stdio.h></winsock2.h></stdio.h></winsock2.h><img src ="http://www.blogjava.net/tingfeng/aggbug/411469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2014-03-25 22:45 <a href="http://www.blogjava.net/tingfeng/articles/411469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows vc++6.0 目录操作函数</title><link>http://www.blogjava.net/tingfeng/articles/262793.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Sun, 29 Mar 2009 16:00:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/262793.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/262793.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/262793.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/262793.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/262793.html</trackback:ping><description><![CDATA[
		<div id="blog_text" class="cnt">
				<p>//Author : cppgp<br />
//Email   : <a href="mailto:cppgp@163.com">cppgp@163.com</a><br />
//Time    : 2007 03 30<br />
//按照字母顺序排列</p>
				<p>
				</p>
				<p>1 . int _access( const char *path, int mode );<br />
功   能 : 测定文件/目录存取权限.<br />
头文件 : #include &lt;io.h&gt;<br />
参   数 : path:文件或者目录<br />
   mode:权限设定,其值如下:<br />
   00 Existence only <br />
   02 Write permission <br />
   04 Read permission <br />
   06 Read and write permission</p>
				<p>返回值 : 拥有该权限返回0<br />
   没有权限返回-1，且设置errno为如下值<br />
   ENOENT 路径/文件不存在<br />
   EACCES 没有相应权限</p>
				<p>
				</p>
				<p>2 . int _chdir( const char *dirname );<br />
功   能 : 更改当前工作目录.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回0<br />
   失败返回-1，且设置errno如下:<br />
   ENOENT 该路径不存在</p>
				<p>
				</p>
				<p>3 . int _chdrive( int drive );<br />
功   能 : 更改当前工作驱动器.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回0<br />
   失败返回-1<br />
注   释 : 参数说明<br />
     drive =1&lt;==&gt; A盘<br />
     drive =2&lt;==&gt; B盘<br />
     drive =3&lt;==&gt; C盘<br />
   如此等等,该函数可以由_chdir代替</p>
				<p>
				</p>
				<p>4 . int _findclose( long handle );<br />
功   能 : 关闭搜寻句柄并释放相应资源<br />
头文件 : #include &lt;io.h&gt;<br />
参   数 : long handle 搜索句柄(通常由紧靠其前的_findfirst()返回,_findfirst()见下)<br />
   fileinfo 文件信息buffer<br />
返回值 : 成功返回0<br />
   出错返回-1,且设置errno为如下值<br />
   ENOENT 没有更多的符合该泛式的文件</p>
				<p>
				</p>
				<p>5 . long _findfirst( char *filespec, struct _finddata_t *fileinfo );<br />
功   能 : 提供与filespec指定入口泛式匹配的第一个文件.通常后继用_findnext函数后续使用来完成某泛式下的文件遍历.<br />
头文件 : #include &lt;io.h&gt;<br />
参   数 : filespec 目标文件规范,可以包含通配符<br />
   fileinfo 文件信息buffer<br />
返回值 : 成功返回唯一的搜索句柄<br />
   出错返回-1,且设置errno为如下值<br />
   ENOENT 该泛式无法匹配<br />
   EINVAL 无效文件名</p>
				<p>
				</p>
				<p>
				</p>
				<p>6 . int _findnext( long handle, struct _finddata_t *fileinfo );<br />
功   能 : 按照前面_findfirst中的泛式规则，查找下一个符合该泛式的文件，并以 此为依据修改fileinfo中的值<br />
头文件 : #include &lt;io.h&gt;<br />
参   数 : long handle 搜索句柄(通常由紧靠其前的_findfirst()返回)<br />
   fileinfo 文件信息buffer<br />
返回值 : 成功返回0<br />
   出错返回-1,且设置errno为如下值<br />
   ENOENT 没有更多的符合该泛式的文件</p>
				<p>
						<br />
				</p>
				<p>7 . char *_getcwd( char *buffer, int maxlen );<br />
功   能 : 获得当前工作目录.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回指向buffer的pointer<br />
   失败返回NULL，且设置errno为以下三个值之一:<br />
   ENODEV 无该设备<br />
   ENOMEM 内存不够<br />
   ERANGE 结果超出范围<br />
注   意 : 当第一个参数为 NULL 时,第二个参数 maxlen 长度设置无效,且函数使用malloc分配足够内存,需要将函数返回值传递给free()函数来释放内存.<br />
   当第一个参数不为 NULL 时,maxlen 指定长度不够函数返回错,设置errno为ERANGE</p>
				<p>
				</p>
				<p>
				</p>
				<p>8 . char *_getdcwd( int drive, char *buffer, int maxlen );<br />
功   能 : 获得指定驱动器的当前工作路径.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回指向buffer的pointer<br />
   失败返回NULL，且设置errno为以下三个值之一:<br />
   ENODEV 无该设备<br />
   ENOMEM 内存不够<br />
   ERANGE 结果超出范围<br />
注   意 : 当第一个参数为 NULL 时,该函数设置errno为ERANGE</p>
				<p>
				</p>
				<p>
				</p>
				<p>9 . int _getdrive( void );<br />
功   能 : 获得当前磁盘驱动器.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 返回驱动器值,1&lt;==&gt;A 2&lt;==&gt;B 如此等等;函数不会出错!</p>
				<p>
				</p>
				<p>
				</p>
				<p>10 . unsigned long _getdrives(void);<br />
功   能 : 获得当前所有驱动器.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 各个位代表对应驱动器,<br />
   bit 0 &lt;==&gt; A<br />
   bit 1 &lt;==&gt; B<br />
   bit 2 &lt;==&gt; C<br />
   ... ...<br />
 注：bit x 表示unsigned long的第x位</p>
				<p>
				</p>
				<p>
				</p>
				<p>11 . int _mkdir( const char *dirname );<br />
功   能 : 创建一个新目录,目录名为dirname.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回0<br />
   失败返回-1，且设置errno为以下三个值之一<br />
   EACCESS 权限不允许<br />
   EEXIST   该目录已存在<br />
   ENOENT   无该文件或目录</p>
				<p>
				</p>
				<p>
				</p>
				<p>12 . int _rmdir( const char *dirname );<br />
功   能 : 删除名为dirname的目录.<br />
头文件 : #include &lt;direct.h&gt;<br />
返回值 : 成功返回0<br />
   失败返回-1，且设置errno为以下三个值之一<br />
   EACCESS    权限不允许<br />
   ENOTEMPTY dirname不是文件夹;<br />
      或者该文件夹不空;<br />
      或者dirname为当前工作文件夹;<br />
      或者dirname为当根文件夹;<br />
   ENOENT     无该文件或目录</p>
				<p>//--end--</p>
				<p>//如有错误，希望指出!</p>
				<p>//联系 : <a href="mailto:cppgp@163.com">cppgp@163.com</a></p>
		</div>
<img src ="http://www.blogjava.net/tingfeng/aggbug/262793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-30 00:00 <a href="http://www.blogjava.net/tingfeng/articles/262793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Visual C++实现注册表简单操作</title><link>http://www.blogjava.net/tingfeng/articles/262259.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Thu, 26 Mar 2009 15:59:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/262259.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/262259.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/262259.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/262259.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/262259.html</trackback:ping><description><![CDATA[一、实现方法<br /><br />
　　对注册表的编程要用到句柄，我们需要通过一个句柄访问注册表键值，当打开或创建一个键值的时候，会返回一个该键的句柄，并且调用和分析键和创建键值，
在分析和创建的同时需要传递句柄到函数。WINDOWS提供预定义的用语---根一级键的保留句柄，如
HKEY_CLASS_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USER等，这些都是与注册表
的根键相对应并且同名的句柄。当访问一个根键的时候，传递这些通用句柄。这就不用打开根键啦，因为他们总是在打开状态下，可使用默认键的句柄访问。<br /><br />
　　Win32
API提供了大约25个有关注册表的函数，他提供了对注册表的读取，写入，删除，以及打开注册表的所有函数，并且可以实现对注册表备份，连接和对远端注册
表进行查看等功能。但是在编程的时候首先需要考虑你是在什么操作系统编辑此类程序，虽然微软的操作系统，如NT和Windows98都是32位操作系统，
但是有些API函数中并不支持98，这点是要注意的。API经历和发展了很多年，有些函数已经重复，比如RegSetValue()及
RegSetValueEx()都是用来设置注册表键值的，两者的区别在于前者是设置注册表键的默认值，仅支持作为数据类型的字符串,而后者不仅继承了前
者的所有功能而且还能对多值或类型进行操作。一般API对比较新的函数都会在后缀追加"Ex"的同样名称函数，建议在编程中均应尽可能的使用高级函数。下
面介绍一些比较常用的操作注册表的API函数：<br /><br />
　　1、RegCloseKey()<br /><br />
　　原型：RegCloseKey(HKEY hKey)<br /><br />
　　解释：关闭指定的主册表键，释放句柄。当对一个或多个键或值操作完成以后，需要关闭其键来进行保存操作结果，关闭一个键后，句柄变为非法，此时应释放句柄。<br /><br />
　　2、RegCreateKeyEx()<br /><br />
　　原型：LONG RegCreateKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD
Reserved,<br />
LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,<br />
PHKEY phkResult, LPDWORD lpdwDisposition );<br /><br />
　　解释：打开指定的键或子键。如果要打开的键不存在的话，本函数会试图建立它。提供该函数是为了向后兼容。所有的WIN32应用程序应使用函数RegCreateKeyEx（）。各参数及返回值的含义如下：<br /><br />
　　各参数及返回值的含义如下：<br /><br />
　　·hKey为主键值，可以取下面的一些数值：HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG、　　
HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USER、
HKEY_PERFORMANCE_DATA(WINNT操作系统)、HKEY_DYN_DATA（WIN9X操作系统）；<br /><br />
　　·参数lpSubKey为一个指向以零结尾的字符串的指针，其中包含将要创建或打开的子键的名称。子键不可以用反斜线（\）开始。该参数可以为NULL；<br /><br />
　　·参数Reserved为保留值，必须设置为0；<br /><br />
　　·参数lpClass为一个指向包含键类型的字符串。如果该键已经存在，则忽略该参数；<br /><br />
　　·参数dwOptions为新创建的键设置一定的属性。可以取下面的一些数值：　　REG_OPTION_NON_VOLATILE
，表示新创建的键为一个非短暂性的键（数据信息保存在文件中，当系统重新启动时，数据信息恢复）；REG_OPTION_VOLATILE，表示新创建的
键为一个短暂性的键（数据信息保存在内存中），Windows95忽略该数值；REG_OPTION_BACKUP_RESTORE
仅在WINNT中支持，可以提供优先级支持；<br /><br />
　　·参数samDesired用来设置对键访问的权限，可以取下面的一些数值：KEY_CREATE_LINK，表示准许生成符号键；KEY_CREATE_SUB_KEY
表示准许生成子键；KEY_ENUMERATE_SUB_KEYS
表示准许生成枚举子键；KEY_EXECUTE
表示准许进行读操作；KEY_NOTIFY表示准许更换通告；　
　KEY_QUERY_VALUE 表示准许查询子键；KEY_ALL_ACCESS
提供完全访问，是上面数值的组合；<br /><br />
　　KEY_READ
是下面数值的组合：KEY_QUERY_VALUE、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFY；　　KEY_SET_VALUE
表示准许设置子键的数值；KEY_WRITE
是下面数值的组合：KEY_SET_VALUE、KEY_CREATE_SUB_KEY；<br /><br />
　　·参数lpSecurityAttributes为一个指向SECURITY_ATTRIBUTES结构的指针，确定返回的句柄是否被子处理过程继承。如果该参数为NULL，则句柄不可以被继承。在WINNT中，该参数可以为新创建的键增加安全的描述；<br /><br />
　　·参数phkResult为一个指向新创建或打开的键的句柄的指针；<br /><br />
　　·参数lpdwDispition指明键是被创建还是被打开的，可以是下面的一些数值：　　REG_CREATE_NEW_KEY
表示键先前不存在，现在被创建；REG_OPENED_EXISTING_KEY
表示键先前已存在，现在被打开。<br /><br />
　　如果该函数调用成功，则返回ERROR_SUCCESS。否则，返回值为文件WINERROR.h中定义的一个非零的错误代码，可以通过设置
FORMAT_MESSAGE_FROM_SYSTEM标识调用FormatMessage（）函数来获取一个对错误的总体描述。<br /><br />
　　3、RegOpenKeyEx（）<br /><br />
　　原型：LONG RegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD
ulOptions,<br />
REGSAM samDesired, PHKEY phkResult );<br /><br />
　　解释：打开一个指定的键，并返回打开键的句柄。<br /><br />
　　各参数及返回值的含义如下：<br /><br />
　　·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />
　　·参数lpSubKey为一个指向以零结尾的字符串的指针，其中包含子键的名称，可以利用反斜线（\）分隔不同的子键名。如果字符串为空，则根据hKey参数创建一个新的句柄。在这种情况下，并不关闭先前打开的句柄；<br /><br />
　　·参数ulOption保留，通常必须设置为0；<br /><br />
　　·参数samDesired的含义同RegCreateKeyEx函数中的samDesired参数；<br /><br />
　　·参数phkResult为一个指针，用来指向打开的键的句柄。可以通过RegCloseKey函数关闭这个句柄；<br /><br />
　　·函数的返回值同RegCreateKeyEx函数的返回值。<br /><br />
　　4、 查询某一个键值：RegQueryValueEx（）<br /><br />
　　原型：LONG RegQueryValueEx(HKEY hKey, LPCTSTR lpValueName,
LPDWORD pReserved, LPDWORD lpType,<br />
LPBYTE lpData, LPDWORD lpcbData );<br /><br />
　　解释：根据要查询的键的句柄，要返回的查询的数据。<br /><br />
　　各个参数及返回值的含义如下：<br /><br />
　　·参数hKey为当前的一个打开的键的句柄，具体数值同RegCreateKeyEx函数的hKey参数；<br /><br />
　　·参数lpVauleName为一个指向非空的包含查询值的名称的字符串指针；<br /><br />
　　·参数lpReserved保留，必须为NULL；<br /><br />
　　·参数lpType为一个指向数据类型的指针，数据类型为下列类型之一：REG_BINARY
二进制数据、REG_DWORD 32位整数、REG_DWORD_LITTLE_ENDIAN
little－endian格式的数据，例如0X12345678以（0X78 0X56 0X34
0X12）方式保存、REG_DWORD_BIG_ENDIAN
big－endian格式的数据，例如0X12345678以（0X12 0X34 0X56
0X78）方式保存、REG_EXPAND_SZ
一个包含未扩展环境变量的字符串、REG_LINK
一个Unicode类型的链接、REG_MULIT_SZ 以两个零结尾的字符串、REG_NONE
无类型数值、REG_RESOURCE_LIST 设备驱动资源列表、REG_SZ
一个以零结尾的字符串根据函数使用的字符集类型的不同而设置为Unicode或ANSI类型的字符串；<br /><br />
　　·参数lpData为一个指向保存返回值的变量的指针。如果不需要返回值，该参数可以为NULL；<br /><br />
　　·参数lpcbData为一个指向保存返回值长度的变量的指针。其中长度以字节为单位。如果数据类型为REG_SZ、REG_MULTI_SZ或
REG_EXPAND_SZ，那么长度也包括结尾的零字符，只有在参数lpData为NULL时，参数lpcbData才可以为NULL；返回值同
RegCreateKeyEx函数的返回值；<br /><br />
　　5、RegSetValueEx（）<br /><br />
　　原型：LONG RegSetValueEx(HKEY hKey, LPCTSTR lpValueName,
LPDWORD lpReserved, DWORD dwType,<br />
const BYTE *lpData, DWORD cbData);<br /><br />
　　解释：设置注册表中的一个键值。<br /><br />
　　各个参数及返回值的含义如下：<br /><br />
　　·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />
　　·参数lpValueName为一个指向包含值名的字符串指针；Reserved保留，通常必须设置为0；<br /><br />
　　·参数dwType确定了设置的值的类型同RegQueryValueKeyEx的lyType参数；<br /><br />
　　·参数lpData为一个指向包含数据的缓冲区的指针；<br /><br />
　　·参数cbData以字节为单位，指定数据的长度；<br /><br />
　　返回值同RegCreateKeyEx函数的返回值。<br /><br />
　　6、RegDeketeKey（）<br /><br />
　　原型：LONG RegDeleteKey（HKEY hKey，LPCTSTR lpSubKEY）；<br /><br />
　　解释：函数RegDeketeKey删除一个键及所有的子键。<br /><br />
　　各个参数及返回值的含义如下：<br /><br />
　　·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />
　　·参数lpSubKey的含义同RegCreateKeyEx函数中的lpSubKey参数。　　<img src ="http://www.blogjava.net/tingfeng/aggbug/262259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-26 23:59 <a href="http://www.blogjava.net/tingfeng/articles/262259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开机启动项</title><link>http://www.blogjava.net/tingfeng/articles/262189.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Thu, 26 Mar 2009 10:25:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/262189.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/262189.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/262189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/262189.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/262189.html</trackback:ping><description><![CDATA[
		<b>【启动项目就是开机的时候系统会在前台或者后台运行的程序</b>】<br />
　　当Windows（操作系统）完成登录过程，进程表中出现了很多的进程！Windows在启动的时候，自动加载了很多程序。<br />
　　许多程序的自启动，给我们带来了很多方便，这是不争的事实，但不是每个自启动的程序对我们都有用；更甚者，也许有病毒或木马在自启动行列！<br />
　　其实Windows2000/XP中的自启动文件，除了从以前系统中遗留下来的Autoexec.bat文件中加载外，按照两个文件夹和9个核心注册表子键来自动加载程序的。<br />
　　【启动项分解】　<br />
　　1）“启动”文件夹──最常见的自启动程序文件夹。<br />
　　它位于系统分区的“documents and Settings－－&gt;User－－&gt;〔开始〕菜单－－&gt;程序”目录下。这时的User指的是登录的用户名。<br />
　　2）“All Users”中的自启动程序文件夹──另一个常见的自启动程序文件夹。<br />
　　它位于系统分区的“documents and Settings－－&gt;All
User－－&gt;〔开始〕菜单－－&gt;程序”目录下。前面提到的“启动”文件夹运行的是登录用户的自启动程序，而“All
Users”中启动的程序是在所有用户下都有效（不论你用什么用户登录）。<br />
　　3）“Load”键值── 一个埋藏得较深的注册表键值。<br />
　　位于〔HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\load〕主键下。<br />
　　4）“Userinit”键值──用户相关<br />
　　它则位于〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows
NT\CurrentVersion\Winlogon\Userinit〕主键下，也是用于系统启动时加载程序的。一般情况下，其默认值为
“userinit.exe”，由于该子键的值中可使用逗号分隔开多个程序，因此，在键值的数值中可加入其它程序。<br />
　　5）“Explorer\Run”键值──与“load”和“Userinit”两个键值不同的是，“Explorer\Run”同时位于〔HKEY_CURRENT_USER〕和〔HKEY_LOCAL_MACHINE〕两个根键中。<br />
　　它在两个中的位置分别为（HKEY_CURRENT_USER\Software
\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run〕和
〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies
\Explorer\Run〕下。<br />
　　6）“RunServicesOnce”子键──它在用户登录前及其它注册表自启动程序加载前面加载。<br />
　　这个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunServicesOnce〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunServicesOnce〕下。<br />
　　7）“RunServices”子键──它也是在用户登录前及其它注册表自启动程序加载前面加载。<br />
　　这个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunServices〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunServices〕下。<br />
　　8）“RunOnce\Setup”子键──其默认值是在用户登录后加载的程序。<br />
　　这个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunOnce\Setup〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunOnce\Setup〕下。<br />
　　9）“RunOnce”子键──许多自启动程序要通过RunOnce子键来完成第一次加载。<br />
　　这个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunOnce〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows\CurrentVersion\RunOnce〕下。位于〔HKEY_CURRENT_USER〕根键下的RunOnce子键在用户登
录扣及其它注册表的Run键值加载程序前加载相关程序，而位于〔HKEY_LOCAL_MACHINE〕主键下的Runonce子键则是在操作系统处理完
其它注册表Run子键及自启动文件夹内的程序后再加载的。在Windows
XP中还多出一个〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion
\RunOnceEX〕子键，其道理相同。<br />
　　10）“Run”子键──目前最常见的自启动程序用于加载的地方。<br />
　　这个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\Run〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows\CurrentVersion\Run〕下。<br />
　　其中位于〔HKEY_CURRENT_USER〕根键下的Run键值紧接着〔HKEY_LOCAL_MACHINE〕主键下的Run键值启动，但两个键值都是在“启动”文件夹之前加载。<br />
　　11）再者就是Windows中加载的服务了，它的级别较高，用于最先加载。<br />
　　其位于〔HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services〕下，看到了吗，你所有的系统服务加载程序都在这里了！<br />
　　12）Windows Shell──系统接口<br />
　　它位于〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows
NT\CurrentVersion\Winlogon\〕下面的Shell字符串类型键值中，基默认值为Explorer.exe，当然可能木马程序会
在此加入自身并以木马参数的形式调用资源管理器，以达到欺骗用户的目的。<br />
　　13）BootExecute──属于启动执行的一个项目 <br />
　　可以通过它来实现启动Natvice程序，Native程序在驱动程序和系统核心加载后将被加载，此时会话管理器(smss.exe)进行windowsNT用户模式并开始按顺序启动native程序<br />
　　它位于注册表中〔HKEY_LOCAL_MACHINE\System
\ControlSet001\Session
Manager\〕下面，有一个名为BootExecute的多字符串值键，它的默认值是"autocheck autochk
*"，用于系统启动时的某些自动检查。这个启动项目里的程序是在系统图形界面完成前就被执行的，所以具有很高的优先级。<br />
　　14）策略组加载程序──打开Gpedit.msc，展开“用户配置——管理模板——系统——登录”，就可以看到“在用户登录时运行这些程序”的项目，你可以在里面添加。<br />
　　在注册表中[HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\Group Policy
Objects\本地User\Software\Microsoft\Windows\CurrentVersion\Policies
\Explorer\Run]你也可以看到相对应的键值。<br />
　　备注：<br />
　　Home版的XP中没有提供gpedit工具，可到网上搜索并下载补丁。<br />
　　快速进入启动项<br />
　　快速进入启动项的方法是在运行中输入 msconfig ，即可看到窗口下的启动项运行项目。<br />
　　[编辑本段]从系统的启动项可以看到什么<br />
　　俗话说“万事开头难”， 俗话也说“良好的开头是成功的一半”，那么XP系统“开头”也就是“启动”能告诉我们什么那。<br />
　　1、msconfig<br />
　　在"开始“-“运行”对话框中输入“msconfig”就打开“系统配置实用程序”。<br />
　　msconfig是Windows系统中的“系统配置实用程序”，它可以自动执行诊断xp系统
的配置问题时所用的常规解决步骤。它管的方面可够宽，包括:一般（常规）、system.ini、win.ini、BOOT.INI、服务、启动。它是
xp系统底层最先启动的程序，可见它的重要性了。这里面可是自启动程序非常喜欢呆的地方。<br />
　　这里我们只介绍一下“启动”<br />
　　系统配置实用程序中的“启动”选项和我们在下面讲的"启动"文件夹并不是同一个东西，在系统配置实用程序中的这个启动项目是Windows系统启动项目的集合地，几乎所有的启动项目部能在这里找到----当然，经过特殊编程处理的程序可以通过另外的方法不在这里显示。<br />
　　打开“启动”标签，“启动项目”中罗列的是开机启动程序的名称，“命令”下是具体的程序附加命令，最后的"位置"就是该程序在注册表中的相应位置了，你可以对可疑的程序进行详细的路径、命令检查，一旦发现错误，就可以用下方的"禁用"来禁止该程序开机时候的加载。<br />
　　一般来讲所有我们可见的程序的列表，你完全可以通过它来管理你的启动程序，换句话，这里可以全部是空的。<br />
　　2、注册表中相应的启动加载项目<br />
　　注册表的启动项目是病毒和木马程序的最爱，非常多的病毒木马的顽固性就是通过注册表来实现的，特别是在安装了新的软件程序，一定不要被程序漂亮的外表迷惑，一定要看清楚它的实质是不是木马的伪装外壳或者是捆绑程序，必要的时候可以根据备份来恢复注册表。<br />
　　我们也可以通过手动的方法来检查注册表中相应的位置，注意同安全、清洁的系统注册表相应键进行比较，如果发现不一致的地方，一定要弄清楚它是什么东西!不要相信写在外面的 “system”、<br />
　　“windows”、“programfiles”等名称，尤其是如果你仔细观察的话，有些字符是不一样的，比如0和o的区别，1和l的区别等，如果经过详细的比较，可以确定它是不明程序的话，不要手软，马上删除。<br />
　　主要的启动加载键值有<br />
　　“Explorer\Run”键值──在HKEY_CURRENT_USER
\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run〕和
〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies
\Explorer\Run〕下。<br />
　　“RunServicesOnce”子键──在
〔HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
\RunServicesOnce〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
\CurrentVersion\RunServicesOnce〕下。<br />
　　“RunServices”子键──在
〔HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
\RunServices〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
\CurrentVersion\RunServices〕下。<br />
　　“RunOnce”子键──在〔HKEY_CURRENT_USER\Software
\Microsoft\Windows\CurrentVersion\RunOnce〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunOnce〕下。<br />
　　“Run”子键──在<br />
　　〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\Run〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows\CurrentVersion\Run〕下。<br />
　　3、“启动”项目<br />
　　在windows的“开始”中有自带的启动文件夹，它是最常见的启动项目。如果在安装程序时设置成开机既启动，这个程序就装入到这个文件夹中，系统启动就会自动地加载相应程序。<br />
　　具体的位置是“开始”菜单中的“所有程序”-“启动”选项。<br />
　　在硬盘上的位置是：C:\Documents and Settings\你的用户名\「开始」菜单\程序\启动。 <br />
　　在注册表中的位置是：<br />
　　HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run。<br />
　　这里最好为空，而且用户要不时地检查一下这里有什么不明的东西。 <br />
　　4、boot.ini<br />
　　当用户的电脑有ghost备份、dos工具或者是双系统时，在开机后就出现个让用户选择，如果不选择就以默认的启动的窗口，(屏幕底部是F8高级启动），boot.ini就控制这个地方。<br />
　　里边的内容一般是<br />
　　timeout=x （x一般在1-5就可以了）<br />
　　default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS <br />
　　[operating systems] <br />
　　multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect………… <br />
　　BOOT.INI是一个非常重要的系统文件，是系统启动时，需要查询的一个系统文件，它告诉启
动程序本计算机有几个操作系统、各系统的位置在哪里等信息。没有它或者误删了，系统还能进行引导，但是一个是只能引导默认的系统，不在有你的备份系统的引
导选择，在一个是每次开机重启时都显示两行字：“boot.ini文件是非法的，现在正从C:/Windows/下启动”，但是速度明显慢了。所以我们平
时除了要对其作必要的备份之外，还要编辑它的方法。特别是在安装多系统时，如果没有按照从低到高(Windows 98、Windows
2000、Windows XP、Windows
2003)的安装顺序，该文件往往会被损坏。如果我们掌握修改和编辑它的办法，就不会到时候无计可施了。<br />
　　5、非法关机以后的“启动”<br />
　　相信很多人都碰到过电脑开机后出现<br />
　　“Checking file system on E:<br />
　　The type of the file system is NTFS……然后是一些数字的变化，最后一行是类似的“??（问号代表数字） allocation units available on disk”，然后就进入系统桌面了”的情况吧。<br />
　　这就是非正常关机,如断电、按热启动键启动、或强制按电源键关机在开机造成的。<br />
　　由于关机的时候E盘里面的程序还在运行，每次开机硬盘都会自动自检，消除错误信息等，而如果非正常关机这些程序没有正常退出，那么下次在开机电脑就要从新执行自检，以便消除消除错误信息，正常的电脑有一次就好了，下次启动就不会出现这种情况了。<br />
　　如果每次开机都出现这样的情况有2个可能：一个是硬盘出现坏道了，硬盘在工作时突然关闭电源，
可能会导致磁头与盘片猛烈磨擦而损坏硬盘，还会使磁头不能正确复位而造成硬盘的划伤，从而在硬盘留下了坏道，但是电脑还能勉强使用，出现这种情况一般只能
更换硬盘了。一个是硬盘没有问题，但是留下了记忆的信息，结果每次都自检，消除的办法就是：开始-运行中输入chkdsk E: /x/f
回车，然后就出现个自动运行的dos窗口，等他运行完毕就没有问题了。<br />
　　chkdsk E: /x /f的意思是Windows发现在E盘里文件系统有问题 ，运行CHKDSK &lt;使用选项/x /f&gt; 来更正这些问题 。<br />
　　对于FAT文件系统，可以使用win自己的磁盘修复来操作，方法是：右击你要操作的盘符，属性-工具，选择查错，把自动修复错误的勾打上，点击开始就可以了。<br />
　　6、其他情况的“启动”<br />
　　当用户打开电源开关后从“启动”到进入桌面以及可以登录网络后，如果花费的时间很长，而且打
开、关闭、拖动一个程序文件的时候显得拖拖拉拉的，有时候还有莫名其妙的从“启动”，这样的“启动”往往说明：（1）加载的启动项目过多，（2）电脑中毒
了，(3)系统盘的空间不足了，（4）机箱该打扫一下了，（5）有关的硬件性能欠佳了……<br /><br /><img src ="http://www.blogjava.net/tingfeng/aggbug/262189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-26 18:25 <a href="http://www.blogjava.net/tingfeng/articles/262189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>文件读取</title><link>http://www.blogjava.net/tingfeng/articles/261578.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Mon, 23 Mar 2009 13:08:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/261578.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/261578.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/261578.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/261578.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/261578.html</trackback:ping><description><![CDATA[
		<h1>fseek</h1>

    
   　　<a target="_blank" href="http://baike.baidu.com/view/15061.htm">函数</a>名: fseek<br />
　　功 能: 重定位流上的文件指针<br />
　　用 法: int fseek(FILE *stream, long offset, int fromwhere);<br />
　　描 述: 函数设置文件指针stream的位置。如果执行成功，stream将指向以fromwhere为基准，偏移offset个字节的位置。如果执行失败(比如offset超过文件自身大小)，则不改变stream指向的位置。<br />
　　返回值: 成功，返回0，否则返回其他值。<br />
　　程序例:<br />
　　#include &lt;stdio.h&gt;<br />
　　long filesize(FILE *stream);<br />
　　int main(void)<br />
　　{<br />
　　FILE *stream;<br />
　　stream = fopen("MYFILE.TXT", "w+");<br />
　　fprintf(stream, "This is a test");<br />
　　printf("Filesize of MYFILE.TXT is %ld bytes\n", filesize(stream));<br />
　　fclose(stream);<br />
　　return 0;<br />
　　}<br />
　　long filesize(FILE *stream)<br />
　　{<br />
　　long curpos, length;<br />
　　curpos = ftell(stream);<br />
　　fseek(stream, 0L, SEEK_END);<br />
　　length = ftell(stream);<br />
　　fseek(stream, curpos, SEEK_SET);<br />
　　return length;<br />
　　}<br />
　　int fseek( FILE *stream, long offset, int origin );<br />
　　第一个参数stream为文件指针<br />
　　第二个参数offset为偏移量，整数表示正向偏移，负数表示负向偏移<br />
　　第三个参数origin设定从文件的哪里开始偏移,可能取值为：SEEK_CUR、 SEEK_END 或 SEEK_SET<br />
　　SEEK_CUR： 当前位置<br />
　　SEEK_END： 文件结尾<br />
　　SEEK_SET： 文件开头<br />
　　其中SEEK_CUR，SEEK_END和SEEK_SET依次为1，2和0。<br /><h1>fread</h1>

    
   　　C语言库函数名: fread<br />
　　功 能: 从一个流中读数据<br />
　　函数原型: int fread(void *ptr, int size, int nitems, FILE *stream);<br />
　　参 数：用于接收数据的地址（字符型指针）（ptr） <br />
　　单个元素的大小（size） <br />
　　元素个数（nitems）<br />
　　提供数据的文件指针（stream）<br />
　　返回值：成功读取的元素个数<br />
　　程序例:<br />
　　#include &lt;string.h&gt;<br />
　　#include &lt;stdio.h&gt;<br />
　　int main(void)<br />
　　{<br />
　　FILE *stream;<br />
　　char msg[] = "this is a test";<br />
　　char buf[20];<br />
　　if ((stream = fopen("DUMMY.FIL", "w+"))<br />
　　== NULL)<br />
　　{<br />
　　fprintf(stderr,<br />
　　"Cannot open output file.\n");<br />
　　return 1;<br />
　　}<br />
　　/* write some data to the file */<br />
　　fwrite(msg, strlen(msg)+1, 1, stream);<br />
　　/* seek to the beginning of the file */<br />
　　fseek(stream, 0, SEEK_SET);<br />
　　/* read the data and display it */<br />
　　fread(buf, strlen(msg)+1, 1,stream);<br />
　　printf("%s\n", buf);<br />
　　fclose(stream);<br />
　　return 0;<br />
　　}<br /><img src ="http://www.blogjava.net/tingfeng/aggbug/261578.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-23 21:08 <a href="http://www.blogjava.net/tingfeng/articles/261578.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>fopen()和fclose()的用法</title><link>http://www.blogjava.net/tingfeng/articles/261323.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Sun, 22 Mar 2009 09:10:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/261323.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/261323.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/261323.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/261323.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/261323.html</trackback:ping><description><![CDATA[fopen()和fclose()的用法<br />1．fopen()函数的用法<br />fopen函数用于打开文件, 其调用格式为:<br />FILE *fopen(char *filename, *type);<br />fopen()函数中第一个形式参数表示文件名, 可以包含路径和文件名两部分。如:"B:TEST.DAT" "C:\\TC\\TEST.DAT"<br />注意：如果将路径写成"C:\TC\TEST.DAT"是不正确的, 这一点要特别注意。fopen函数用来打开一个文件，其调用的一般形式为： 文件指针名=fopen(文件名，使用文件方式)其中，“文件指针名”必须是被说明为FILE 类型的指针变量，“文件名”是被打开文件的文件名。“使用文件方式”是指文件的类型和操作要求。“文件名”是字符串常量或字符串数组。例如： <br />FILE *fp；<br />fp=("file a","r");<br />其意义是在当前目录下打开文件file a， 只允许进行“读”操作，并使fp指向该文件。<br />又如：<br />FILE *fphzk<br />fphzk=("c:\\hzk16',"rb")<br />其意义是打开C驱动器磁盘的根目录下的文件hzk16， 这是一个二进制文件，只允许按二进制方式进行读操作。<br />两个反斜线“\\ ”中的第一个表示转义字符，第二个表示根目录。使用文件的方式共有12种，下面给出了它们的符号和意义。<br />第二个形式参数表示打开文件的类型。关于文件类型的规定参见下表。 <br />表 文件操作类型<br />━━━━━━━━━━━━━━━━━━━━━━━━━━━━<br />字符                含义<br />────────────────────────────<br />"r"           打开文字文件只读<br />"w"           创建文字文件只写<br />"a"           增补, 如果文件不存在则创建一个<br />"r+"          打开一个文字文件读/写<br />"w+"          创建一个文字文件读/写<br />"a+"         打开或创建一个文件增补<br />"b"           二进制文件(可以和上面每一项合用)<br />"t"           文这文件(默认项)<br />━━━━━━━━━━━━━━━━━━━━━━━━━━━━<br />文件使用方式 　　　　　　　意 义<br />“rt”　　　　　　只读打开一个文本文件，只允许读数据 <br />“wt”　　　　　　只写打开或建立一个文本文件，只允许写数据<br />“at”　　　　　　追加打开一个文本文件，并在文件末尾写数据<br />“rb”　　　　　　只读打开一个二进制文件，只允许读数据<br />“wb”　　　　 　 只写打开或建立一个二进制文件，只允许写数据<br />“ab” 　　　　 　追加打开一个二进制文件，并在文件末尾写数据<br />“rt+”　　　　　 读写打开一个文本文件，允许读和写<br />“wt+”　　　　　 读写打开或建立一个文本文件，允许读写<br />“at+”　　　　　 读写打开一个文本文件，允许读，或在文件末追加数 据<br />“rb+”　　　　　 读写打开一个二进制文件，允许读和写 <br />“wb+”　　　　　 读写打开或建立一个二进制文件，允许读和写<br />“ab+” 　　　　　读写打开一个二进制文件，允许读，或在文件末追加数据<br /><br />对于文件使用方式有以下几点说明：<br />1. 文件使用方式由r,w,a,t,b，+六个字符拼成，各字符的含义是：<br />r(read): 读<br />w(write): 写<br />a(append): 追加<br />t(text): 文本文件，可省略不写<br />b(banary): 二进制文件<br />+: 读和写<br /><br />2. 凡用“r”打开一个文件时，该文件必须已经存在， 且只能从该文件读出。<br />3. 用“w”打开的文件只能向该文件写入。 若打开的文件不存在，则以指定的文件名建立该文件，若打开的文件已经存在，则将该文件删去，重建一个新文件。<br />4. 若要向一个已存在的文件追加新的信息，只能用“a ”方式打开文件。但此时该文件必须是存在的，否则将会出错。<br />5. 在打开一个文件时，如果出错，fopen将返回一个空指针值NULL。在程序中可以用这一信息来判别是否完成打开文件的工作，并作相应的处理。因此常用以下程序段打开文件：<br />if((fp=fopen("c:\\hzk16","rb")==NULL)<br />{<br />printf("\nerror on open c:\\hzk16 file!");<br />getch();<br />exit(1);<br />}<br />这段程序的意义是，如果返回的指针为空，表示不能打开C盘根目录下的hzk16文件，则给出提示信息“error on open c:\ hzk16file!”，下一行getch()的功能是从键盘输入一个字符，但不在屏幕上显示。在这里，该行的作用是等待， 只有当用户从键盘敲任一键时，程序才继续执行， 因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。<br /><br />6. 把一个文本文件读入内存时，要将ASCII码转换成二进制码， 而把文件以文本方式写入磁盘时，也要把二进制码转换成ASCII码，因此文本文件的读写要花费较多的转换时间。对二进制文件的读写不存在这种转换。<br /><br />7. 标准输入文件(键盘)，标准输出文件(显示器 )，标准出错输出(出错信息)是由系统打开的，可直接使用。文件关闭函数ｆｃｌｏｓｅ文件一旦使用完毕，应用关闭文件函数把文件关闭， 以避免文件的数据丢失等错误。<br />如果要打开一个CCDOS子目录中, 文件名为CLIB的二进制文件, 可写成:<br />fopen("c:\\ccdos\\clib", "rb");<br />如果成功的打开一个文件, fopen()函数返回文件指针,   否则返回空指针(NULL)。由此可判断文件打开是否成功。<br />2. fclose()函数<br />fclose()函数用来关闭一个由fopen()函数打开的文件 , 其调用格式为:<br />nt fclose(FILE *stream);<br />该函数返回一个整型数。当文件关闭成功时, 返回0,　否则返回一个非零值。可以根据函数的返回值判断文件是否关闭成功。<br />例子：<br />FILE *fpOut=fopen(“c:\\a.txt”,”wt+”);<br />Int a=1;<br />Fprintf(fpOut,”%d”,a);<br />Fclose(fpOut);<br />                <br /><br /><img src ="http://www.blogjava.net/tingfeng/aggbug/261323.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-22 17:10 <a href="http://www.blogjava.net/tingfeng/articles/261323.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++字符串处理函数</title><link>http://www.blogjava.net/tingfeng/articles/261266.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Sat, 21 Mar 2009 14:55:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/261266.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/261266.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/261266.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/261266.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/261266.html</trackback:ping><description><![CDATA[
		<p>
				<b>1、strcpy(char destination[], const char   source[]);</b>
		</p>
		<p>
				<strong>strcpy：</strong>将字符串source拷贝到字符串destination中。<br />
  strcpy函数应用举例<br />
  原型：strcpy(char   destination[], const char source[]);<br />
  功能：将字符串source拷贝到字符串destination中<br />
  例程： </p>
		<p> #include   &lt;iostream.h&gt;<br />
  #include &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
　
char   str1[10] = {  "TsinghuaOK"};<br />
　
char str2[10] =   {  "Computer"};<br />
　
cout  &lt;&lt;strcpy(str1,str2)&lt;&lt;endl;<br />
  }</p>
		<p>运行结果是:Computer</p>
		<p>第二个字符串将覆盖掉第一个字符串的所有内容！</p>
		<p>注意：在定义数组时，字符数组1的字符串长度必须大于或等于字符串2的字符串长度。不能用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。所有字符串处理函数都包含在头文件string.h中。</p>
		<p>
				<b>
						<br />
  2、strncpy(char destination[], const char source[], int numchars);</b>
		</p>
		<p>
				<strong>strncpy：</strong>将字符串source中前numchars个字符拷贝到字符串destination中。<br />
  strncpy函数应用举例<br />
  原型：strncpy(char   destination[], const char source[], int   numchars);<br />
  功能：将字符串source中前numchars个字符拷贝到字符串destination中<br />
  例程： </p>
		<p>#include &lt;iostream.h&gt;<br />
  #include   &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
　
char str1[10] = {  "Tsinghua   "};<br />
　
char str2[10] =   {  "Computer"};<br />
　
cout  &lt;&lt;strncpy(str1,str2,3)&lt;&lt;endl;<br />
  }</p>
		<p>运行结果：Comnghua</p>
		<p>注意：字符串source中前numchars个字符将覆盖掉字符串destination中前numchars个字符！</p>
		<p>
				<b>3、strcat(char target[], const char source[]);</b>
		</p>
		<p>
				<strong>strcat:</strong>将字符串source接到字符串target的后面。<br />
  strcat函数应用举例<br />
  原型：strcat(char   target[], const char source[]);<br />
  功能：将字符串source接到字符串target的后面<br />
  例程：</p>
		<p>#include &lt;iostream.h&gt;<br />
  #include   &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
　
char str1[] = {  "Tsinghua   "};<br />
　
char str2[] =   {  "Computer"};<br />
　
cout  &lt;&lt;strcpy(str1,str2)&lt;&lt;endl;<br />
  }</p>
		<p>运行结果：Tsinghua Computer</p>
		<p>注意：在定义字符数组1的长度时应该考虑字符数组2的长度，因为连接后新字符串的长度为两个字符串长度之和。进行字符串连接后，字符串1的结尾符将自动被去掉，在结尾串末尾保留新字符串后面一个结尾符。 <br /><br /><b>4、strncat(char target[], const char source[], int numchars);</b></p>
		<p>
				<strong>strncat:</strong>将字符串source的前numchars个字符接到字符串target的后面。<br />
  strncat函数应用举例：<br />
  原型：strncat(char   target[], const char source[], int   numchars);<br />
  功能：将字符串source的前numchars个字符接到字符串target的后面<br />
  例程：</p>
		<p>#include &lt;iostream.h&gt;<br />
  #include   &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
　
char str1[] = {  "Tsinghua   "};<br />
　
char str2[] =   {  "Computer"};<br />
　
cout  &lt;&lt;strncat(str1,str2,3)&lt;&lt;endl;<br />
  }</p>
		<p>运行结果：Tsinghua Com</p>
		<p>
				<br />
				<b>5、int strcmp(const char firststring[], const char secondstring);</b>
		</p>
		<p>
				<strong>strcmp：</strong>比较两个字符串firststring和secondstring。<br />
  strcmp函数应用举例<br />
  原型：int   strcmp(const char firststring[], const char   secondstring);<br />
  功能：比较两个字符串firststring和secondstring<br />
  例程： </p>
		<p>#include &lt;iostream.h&gt;<br />
  #include   &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
　
char buf1[] =   "aaa";<br />
　
char   buf2[] =   "bbb";<br />
　
char buf3[] =   "ccc";<br />
　
int ptr;  <br />
　
ptr =   strcmp(buf2,buf1);  <br />
　
if(ptr   &gt; 0)<br />
　　
cout  &lt;&lt;"Buffer 2 is greater than   buffer 1"&lt;&lt;endl;<br />
　
else  <br />
　　
cout  &lt;&lt;"Buffer 2 is less than buffer   1"&lt;&lt;endl;<br />
　
ptr = strcmp(buf2,buf3);  <br />
　
if(ptr   &gt;   0)<br />
　　
cout  &lt;&lt;"Buffer 2 is greater than buffer   3"&lt;&lt;endl;<br />
　
else  <br />
　　
cout  &lt;&lt;"Buffer 2 is less than buffer   3"&lt;&lt;endl;<br />
  }</p>
		<p>运行结果是:Buffer 2 is less than buffer   1 <br />
                   
  Buffer 2 is greater than buffer 3</p>
		<p>
				<b>6、strlen( const char string[] );</b>
		</p>
		<p>
				<strong>strlen：</strong>统计字符串string中字符的个数。　　<br />
  strlen函数应用举例<br />
  原型：strlen(   const char string[] );<br />
  功能：统计字符串string中字符的个数<br />
  例程： </p>
		<p>#include &lt;iostream.h&gt;<br />
  #include   &lt;string.h&gt;<br />
  void main(void)<br />
  {<br />
  
char str[100];  <br />
    
cout  &lt;&lt;"请输入一个字符串:";<br />
  
cin  &gt;&gt;str;<br />
  
cout  &lt;&lt;"The length of   the string is :"&lt;&lt;strlen(str)&lt;&lt;"个"&lt;&lt;endl;<br />
  }</p>
		<p>运行结果The length of the string is x (x为你输入的字符总数字)</p>
		<p>注意：strlen函数的功能是计算字符串的实际长度，不包括'\0'在内。另外，strlen函数也可以直接测试字符串常量的长度，如：strlen("Welcome")。
</p>
<img src ="http://www.blogjava.net/tingfeng/aggbug/261266.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-21 22:55 <a href="http://www.blogjava.net/tingfeng/articles/261266.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CString 操作指南</title><link>http://www.blogjava.net/tingfeng/articles/261265.html</link><dc:creator>听风</dc:creator><author>听风</author><pubDate>Sat, 21 Mar 2009 14:49:00 GMT</pubDate><guid>http://www.blogjava.net/tingfeng/articles/261265.html</guid><wfw:comment>http://www.blogjava.net/tingfeng/comments/261265.html</wfw:comment><comments>http://www.blogjava.net/tingfeng/articles/261265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/tingfeng/comments/commentRss/261265.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/tingfeng/services/trackbacks/261265.html</trackback:ping><description><![CDATA[原著：Joseph M. Newcomer<br /><br />
      翻译：<a href="mailto:littleloach@sina.com">littleloach</a><p>原文出处：<a target="_blank" href="http://www.codeproject.com/string/cstringmgmt.asp">codeproject：CString 
      Management</a><br /><br /><br />
      通过阅读本文你可以学习如何有效地使用 CString。<br /><br />
　　CString
是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作，使得MFC在做字符串操作的时候方便了很多。不管怎样，使用CString有很多特殊
的技巧，特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。<br />
      　　使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册，但囊括了大部分常见基本问题。<br /><br />
      这篇文章包括以下内容：</p><ol><li><p align="left"><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%BF%9E%E6%8E%A5">CString 对象的连接</a></p></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2">格式化字符串（包括 int 型转化为 CString ）</a></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20%E5%9E%8B%E8%BD%AC%E5%8C%96%E6%88%90%20int%20%E5%9E%8B">CString 型转化成 int 型</a></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20%E5%9E%8B%E5%92%8C%20char*%20%E7%B1%BB%E5%9E%8B%E7%9A%84%E7%9B%B8%E4%BA%92%E8%BD%AC%E5%8C%96">CString 型和 char* 类型的相互转化</a><br /><dir><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#char*%20%E8%BD%AC%E5%8C%96%E4%B8%BA%20CString">char* 转化成 CString</a></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%E8%BD%AC%E5%8C%96%E6%88%90char*%20%E4%B9%8B%E4%B8%80">CString 转化成 char* 之一：使用LPCTSTR强制转化</a></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%E8%BD%AC%E5%8C%96%E6%88%90char*%20%E4%B9%8B%E4%BA%8C">CString 转化成 char* 之二：使用CString对象的GetBuffer方法</a></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20to%20char%20*%20%E4%B9%8B%E4%B8%89">CString 转化成 char* 之三: 和控件的接口</a></li></dir></li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20%E5%9E%8B%E8%BD%AC%E5%8C%96%E6%88%90%20BSTR%20%E5%9E%8B">CString 型转化成 BSTR 型</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#BSTR%20%E5%9E%8B%E8%BD%AC%E5%8C%96%E6%88%90%20CString%20%E5%9E%8B">BSTR 型转化成 CString 型</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#VARIANT%20%E5%9E%8B%E8%BD%AC%E5%8C%96%E6%88%90%20CString%20%E5%9E%8B">VARIANT 型转化成 CString 型</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#%E8%BD%BD%E5%85%A5%E5%AD%97%E7%AC%A6%E4%B8%B2%E8%A1%A8%E8%B5%84%E6%BA%90">载入字符串表资源</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%20%E5%92%8C%E4%B8%B4%E6%97%B6%E5%AF%B9%E8%B1%A1">CString 和临时对象</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#CString%E7%9A%84%E6%95%88%E7%8E%87">CString 的效率</a>；</li><li><a href="http://www.vckbase.com/document/viewdoc/?id=1094#%E6%80%BB%E7%BB%93">总结</a></li></ol><p>下面我分别讨论。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 1、<b><a name="CString 对象的连接">CString 对象的连接</a></b><br /><br />
      　　能体现出 CString 类型方便性特点的一个方面就字符串的连接，使用 CString 类型，你能很方便地连接两个字符串，正如下面的例子：</p><pre>CString gray("Gray");<br />CString cat("Cat");<br />CString graycat = gray + cat;</pre><p>要比用下面的方法好得多：</p><pre>char gray[] = "Gray";<br />char cat[] = "Cat";<br />char * graycat = malloc(strlen(gray) + strlen(cat) + 1);<br />strcpy(graycat, gray);<br />strcat(graycat, cat);</pre><p><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 2、<b><a name="格式化字符串">格式化字符串</a></b><br /><br />
      　　与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串，还不如用 CString 对象的Format()方法：</p><pre>CString s;<br />s.Format(_T("The total is %d"), total);</pre><p>　　用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大，这些工作由CString类替你完成。<br />
      　　格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧，比如，把一个整数转化成CString类型，可用如下方法：</p><pre>CString s;<br />s.Format(_T("%d"), total);</pre><p>
      　　我总是对我的字符串使用_T()宏，这是为了让我的代码至少有Unicode的意识，当然，关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的：</p><pre>#define _T(x) x // 非Unicode版本（non-Unicode version）</pre><p>而在Unicode环境下是如下定义的：</p><pre>#define _T(x) L##x // Unicode版本（Unicode version）</pre><p>所以在Unicode环境下，它的效果就相当于：</p><pre>s.Format(L"%d", total);</pre><p>　
　如果你认为你的程序可能在Unicode的环境下运行，那么开始在意用 Unicode 编码。比如说，不要用 sizeof()
操作符来获得字符串的长度，因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节，比如在我需要获得字符长度
的时候，我会用一个叫做DIM的宏，这个宏是在我的dim.h文件中定义的，我会在我写的所有程序中都包含这个文件：</p><pre>#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )</pre>
      　　这个宏不仅可以用来解决Unicode的字符串长度的问题，也可以用在编译时定义的表格上，它可以获得表格的项数，如下：<br /><pre>class Whatever { ... };<br />Whatever data[] = {<br />   { ... },<br />    ...<br />   { ... },<br />};<br />for(int i = 0; i &lt; DIM(data); i++) // 扫描表格寻找匹配项。<br /></pre>
      　　这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用，如果你传递字符个数给它，它将不能正常工作。如下：<pre>TCHAR data[20];<br />lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!<br />lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT<br />WriteFile(f, data, DIM(data), &amp;bytesWritten, NULL); // WRONG!<br />WriteFile(f, data, sizeof(data), &amp;bytesWritten, NULL); // RIGHT</pre><p>造成以上原因是因为lstrcpyn需要一个字符个数作为参数，但是WriteFile却需要字节数作为参数。<br />
      同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度，你可能会认为你应该这样做：</p><pre>WriteFile(f, data, lstrlen(data), &amp;bytesWritten, NULL); // WRONG</pre><p>但是在Unicode环境下，它不会正常工作。正确的做法应该是这样：</p><pre>WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &amp;bytesWritten, NULL); // RIGHT</pre><p>
　　因为WriteFile需要的是一个以字节为单位的长度。（可能有些人会想“在非Unicode的环境下运行这行代码，就意味着总是在做一个多余的乘
1操作，这样不会降低程序的效率吗？”这种想法是多余的，你必须要了解编译器实际上做了什么，没有哪一个C或C++编译器会把这种无聊的乘1操作留在代码
中。在Unicode环境下运行的时候，你也不必担心那个乘2操作会降低程序的效率，记住，这只是一个左移一位的操作而已，编译器也很乐意为你做这种替
换。）<br />
　　使用_T宏并不是意味着你已经创建了一个Unicode的程序，你只是创建了一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编
译你的程序的话，得到的将是一个普通的8-bit的应用程序（这里的8-bit指的只是8位的字符编码，并不是指8位的计算机系统）；当你在
Unicode环境下编译你的程序时，你才会得到一个Unicode的程序。记住，CString 在 Unicode
环境下，里面包含的可都是16位的字符哦。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 3、<b><a name="CString 型转化成 int 型">CString 型转化成 int 型</a></b><br /><br />
      　　把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。<br />
　　虽然通常你怀疑使用_atoi()函数是一个好的选择，它也很少会是一个正确的选择。如果你准备使用 Unicode
字符，你应该用_ttoi()，它在 ANSI 编码系统中被编译成_atoi()，而在 Unicode
编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol()，它们都能把字符串转化成任意进制的长整数（如二进制、八
进制、十进制或十六进制），不同点在于前者转化后的数据是无符号的（unsigned），而后者相反。看下面的例子：</p><pre>CString hex = _T("FAB");<br />CString decimal = _T("4011");<br />ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));</pre><p><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 4、<b><a name="CString 型和 char* 类型的相互转化">CString 型和 char* 类型的相互转化</a></b><br /><br />
      　　这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助，很多问题你不需要深入的去考虑它，直接拿来用就行了，但是如果你不能深入了解它的运行机制，又会有很多问题让你迷惑，特别是有些看起来没有问题的代码，却偏偏不能正常工作。<br />
      比如，你会奇怪为什么不能写向下面这样的代码呢：</p><pre>CString graycat = "Gray" + "Cat";</pre><p>或者这样：</p><pre>CString graycat("Gray" + "Cat");</pre><p>　　事实上，编译器将抱怨上面的这些尝试。为什么呢？因为针对CString 和 LPCTSTR数据类型的各种各样的组合，“ +” 运算符
      被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型，它是底层数据类型。你不能对基本数据（如 int、char 或者 char*）类型重载 C++ 
      的运算符。你可以象下面这样做：</p><pre>CString graycat = CString("Gray") + CString("Cat");</pre><p>或者这样：</p><pre>CString graycat = CString("Gray") + "Cat";</pre><p>研究一番就会发现：“ +”总是使用在至少有一个 CString 对象和一个
      LPCSTR 的场合。<br /><br />
      注意，编写有 Unicode 意识的代码总是一件好事，比如：</p><pre>CString graycat = CString(_T("Gray")) + _T("Cat");</pre><p>这将使得你的代码可以直接移植。<br /><br /><i><b><a name="char* 转化为 CString">char* 转化为 CString</a></b></i><br /><br />
      　　现在你有一个 char* 类型的数据，或者说一个字符串。怎么样创建 CString 对象呢？这里有一些例子：</p><pre>char * p = "This is a test";</pre><p>或者象下面这样更具有 Unicode 意识：</p><pre>TCHAR * p = _T("This is a test")</pre><p>或</p><pre>LPTSTR p = _T("This is a test");</pre><p>你可以使用下面任意一种写法：</p><pre>CString s = "This is a test"; // 8-bit only<br />CString s = _T("This is a test"); // Unicode-aware<br />CString s("This is a test"); // 8-bit only<br />CString s(_T("This is a test")); // Unicode-aware<br />CString s = p;<br />CString s(p);</pre><p>　　用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是，字符的赋值总是被拷贝到 CString 对象中去的，所以你可以象下面这样操作：</p><pre>TCHAR * p = _T("Gray");<br />CString s(p);<br />p = _T("Cat");<br />s += p;</pre><p>结果字符串肯定是“GrayCat”。<br /><br />
      CString 类还有几个其它的构造函数，但是这里我们不考虑它，如果你有兴趣可以自己查看相关文档。<br /><br />
      事实上，CString 类的构造函数比我展示的要复杂，比如：</p><pre>CString s = "This is a test"; </pre><p>　　这是很草率的编码，但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 
      操作将 8 位字符串转换成 16 位字符串。不管怎样，如果 char * 指针是网络上传输的 8 位数据，这种转换是很有用的。<br /><br /><i><b><a name="CString转化成char* 之一">CString 转化成 char* 之一</a>：</b></i>强制类型转换为 LPCTSTR；<br /><br />
      　　这是一种略微硬性的转换，有关“正确”的做法，人们在认识上还存在许多混乱，正确的使用方法有很多，但错误的使用方法可能与正确的使用方法一样多。<br />
      　　我们首先要了解 CString 是一种很特殊的 C++ 对象，它里面包含了三个值：一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。
      有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数（因为字符串结尾有一个NULL字符）。字符记数和缓冲区长度被巧妙隐藏。<br />
      　　除非你做一些特殊的操作，否则你不可能知道给CString对象分配的缓冲区的长度。这样，即使你获得了该0缓冲的地址，你也无法更改其中的内容，不能截短字符串，也
      绝对没有办法加长它的内容，否则第一时间就会看到溢出。<br />
      　　LPCTSTR 操作符（或者更明确地说就是 TCHAR * 操作符）在 CString 类中被重载了，该操作符的定义是返回缓冲区的地址，因此，如果你需要一个指向 CString 的
      字符串指针的话，可以这样做：</p><pre>CString s("GrayCat");<br />LPCTSTR p = s;</pre><p>
      　　它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时，C++规测容许这种选择。比如，你可以将（浮点数）定义为将某个复数
      （有一对浮点数）进行强制类型转换后只返回该复数的第一个浮点数（也就是其实部）。可以象下面这样：</p><pre>Complex c(1.2f, 4.8f);<br />float realpart = c;</pre><p>如果(float)操作符定义正确的话，那么实部的的值应该是1.2。<br />
      　　这种强制转化适合所有这种情况，例如，任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。
      于是，你可能有这样一个函数（也许在某个你买来的DLL中）：</p><pre>BOOL DoSomethingCool(LPCTSTR s);</pre><p>你象下面这样调用它：</p><pre>CString file("c:\\myfiles\\coolstuff")<br />BOOL result = DoSomethingCool(file);</pre><p>　　它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数，因此 LPCTSTR 
      被应用于该参数，在 MFC 中就是返回的串地址。<br /><br />
      如果你要格式化字符串怎么办呢？</p><pre>CString graycat("GrayCat");<br />CString s;<br />s.Format("Mew! I love %s", graycat);</pre><p>　　注意由于在可变参数列表中的值（在函数说明中是以“...”表示的）并没有隐含一个强制类型转换操作符。你会得到什么结果呢？<br />
      　　一个令人惊讶的结果，我们得到的实际结果串是：</p><pre>"Mew! I love GrayCat"。</pre><p>　　因为 MFC 的设计者们在设计 CString 数据类型时非常小心， CString 类型表达式求值后指向了字符串，所以这里看不到任何象 
      Format 或 sprintf 中的强制类型转换，你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。<br />
      　　有一件事情你是不能做的，那就是修改字符串。比如，你可能会尝试用“,”代替“.”（不要做这样的，如果你在乎国际化问题，你应该使用十进制转换的 
      National Language Support 特性，），下面是个简单的例子：</p><pre>CString v("1.00"); // 货币金额，两位小数<br />LPCTSTR p = v;<br />p[lstrlen(p) - 3] = '','';</pre><p>　　这时编译器会报错，因为你赋值了一个常量串。如果你做如下尝试，编译器也会错：</p><pre>strcat(p, "each");</pre><p>　　因为 strcat 的第一个参数应该是 LPTSTR 类型的数据，而你却给了一个 LPCTSTR。<br /><br />
      　　不要试图钻这个错误消息的牛角尖，这只会使你自己陷入麻烦！<br /><br />
　　原因是缓冲有一个计数，它是不可存取的（它位于 CString
地址之下的一个隐藏区域），如果你改变这个串，缓冲中的字符计数不会反映所做的修改。此外，如果字符串长度恰好是该字符串物理限制的长度（梢后还会讲到这
个问题），那么扩展该字符串将改写缓冲以外的任何数据，那是你无权进行写操作的内存（不对吗？），你会毁换坏不属于你的内存。这是应用程序真正的死亡处
方。</p><p><i><b><a name="CString转化成char* 之二">CString转化成char* 之二</a>：</b></i>使用 CString 对象的 GetBuffer 方法；<br /><br />
      　　如果你需要修改 CString 中的内容，它有一个特殊的方法可以使用，那就是 GetBuffer，它的作用是返回一个可写的缓冲指针。
      如果你只是打算修改字符或者截短字符串，你完全可以这样做：</p><pre>CString s(_T("File.ext"));<br />LPTSTR p = s.GetBuffer();<br />LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...<br />if(p != NULL)<br />*p = _T(''\0'');<br />s.ReleaseBuffer();</pre><p>
　　这是 GetBuffer 的第一种用法，也是最简单的一种，不用给它传递参数，它使用默认值
0，意思是：“给我这个字符串的指针，我保证不加长它”。当你调用 ReleaseBuffer 时，字符串的实际长度会被重新计算，然后存入
CString 对象中。<br />
      　　必须强调一点，在 GetBuffer 和 ReleaseBuffer 之间这个范围，一定不能使用你要操作的这个缓冲的 CString 
      对象的任何方法。因为 ReleaseBuffer 被调用之前，该 CString 对象的完整性得不到保障。研究以下代码：</p><pre>CString s(...);<br /><br />LPTSTR p = s.GetBuffer();<br /><br />//... 这个指针 p 发生了很多事情<br /><br />int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!<br /><br />s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!<br /><br />s.ReleaseBuffer(); // 现在应该 OK<br /><br />int m = s.GetLength(); // 这个结果可以保证是正确的。<br /><br />s.TrimRight(); // 将正常工作。</pre><p>　　假设你想增加字符串的长度，你首先要知道这个字符串可能会有多长，好比是声明字符串数组的时候用：</p><pre>char buffer[1024];</pre><p>表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法：</p><pre>LPTSTR p = s.GetBuffer(1024);</pre><p>　　调用这个函数后，你不仅获得了字符串缓冲区的指针，而且同时还获得了长度至少为 1024 个字符的空间（注意，我说的是“字符”，而不是“字节”，因为 CString 
      是以隐含方式感知 Unicode 的）。<br />
      　　同时，还应该注意的是，如果你有一个常量串指针，这个串本身的值被存储在只读内存中，如果试图存储它，即使你已经调用了 GetBuffer 
      ，并获得一个只读内存的指针，存入操作会失败，并报告存取错误。我没有在 CString 上证明这一点，但我看到过大把的 C 程序员经常犯这个错误。<br />
      　　C 程序员有一个通病是分配一个固定长度的缓冲，对它进行 sprintf 操作，然后将它赋值给一个 CString：</p><pre>char buffer[256];<br />sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节<br />CString s = buffer;</pre><p>虽然更好的形式可以这么做：</p><pre>CString s;<br />s.Format(_T("%...."), args, ...);</pre><p>如果你的字符串长度万一超过 256 个字符的时候，不会破坏堆栈。<br /><br />
      　　另外一个常见的错误是：既然固定大小的内存不工作，那么就采用动态分配字节，这种做法弊端更大：</p><pre>int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;<br /><br />char * buffer = new char[len];<br /><br />sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);<br /><br />CString s = buffer;<br /><br />......<br /><br />delete [] buffer;</pre><p>它可以能被简单地写成：</p><pre>CString s;<br /><br />s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);</pre><p>　　需要注意 sprintf 例子都不是 Unicode 就绪的，尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串，但是基本
      思路仍然是在走弯路，这这样很容易出错。<br /><br /><i><b><a name="CString to char * 之三">CString to char * 之三</a>：</b></i>和控件的接口；<br /><br />
      　　我们经常需要把一个 CString 的值传递给一个控件，比如，CTreeCtrl。MFC为我们提供了很多便利来重载这个操作，但是
      在大多数情况下，你使用“原始”形式的更新，因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下：</p><pre>TVINSERTITEMSTRUCT tvi;<br />CString s;<br />// ... 为s赋一些值。<br />tvi.item.pszText = s; // Compiler yells at you here<br />// ... 填写tvi的其他域<br />HTREEITEM ti = c_MyTree.InsertItem(&amp;tvi);</pre><p>　　为什么编译器会报错呢？明明看起来很完美的用法啊！但是事实上如果你看看 TVITEM 结构的定义你就会明白，在 TVITEM 结构中 pszText 成员的声明如下：</p><pre>LPTSTR pszText;<br />int cchTextMax;</pre><p>　　因此，赋值不是赋给一个 LPCTSTR 类型的变量，而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧，你说，那我就改成这样：</p><pre>tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。</pre><p>　　编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量，这种操作在C或C++中是被禁止的。你不能用这种方法
      来滥用常量指针与非常量指针概念，否则，会扰乱编译器的优化机制，使之不知如何优化你的程序。比如，如果你这么做：</p><pre>const int i = ...;<br />//... do lots of stuff<br />... = a[i]; // usage 1<br />// ... lots more stuff<br />... = a[i]; // usage 2</pre><p>　　那么，编译器会以为既然 i 是 const ，所以 usage1和usage2的值是相同的，并且它甚至能事先计算好 usage1 处的 
      a[i] 的地址，然后保留着在后面的 usage2 处使用，而不是重新计算。如果你按如下方式写的话：</p><pre>const int i = ...;<br />int * p = &amp;i;<br />//... do lots of stuff<br />... = a[i]; // usage 1<br />// ... lots more stuff<br />(*p)++; // mess over compiler''s assumption<br />// ... and other stuff<br />... = a[i]; // usage 2</pre><p>　　编译器将认为 i 是常量，从而 a[i] 的位置也是常量，这样间接地破坏了先前的假设。因此，你的程序将会在 debug 
      编译模式（没有优化）和 release 编译模式（完全优化）中反映出不同的行为，这种情况可不好，所以当你试图把指向 i 的指针赋值给一个
      可修改的引用时，会被编译器诊断为这是一种伪造。这就是为什么（LPCTSTR）强制类型转化不起作用的原因。<br />
      　　为什么不把该成员声明成 LPCTSTR 类型呢？因为这个结构被用于读写控件。当你向控件写数据时，文本指针实际上被当成 LPCTSTR，而当你从控件读数据
      时，你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。<br /><br />
      因此，你会常常在我的代码中看到如下的用法：</p><pre>tvi.item.pszText = (LPTSTR)(LPCTSTR)s;</pre><p>　　它把 CString 强制类型转化成 LPCTSTR，也就是说先获得改字符串的地址，然后再强制类型转化成 LPTSTR，以便可以对之进行赋值操作。
      注意这只有在使用 Set 或 Insert 之类的方法才有效！如果你试图获取数据，则不能这么做。<br />
      　　如果你打算获取存储在控件中的数据，则方法稍有不同，例如，对某个 CTreeCtrl 使用 GetItem 方法，我想获取项目的文本。我知道这些
      文本的长度不会超过 MY_LIMIT，因此我可以这样写：</p><pre>TVITEM tvi;<br />// ... assorted initialization of other fields of tvi<br />tvi.pszText = s.GetBuffer(MY_LIMIT);<br />tvi.cchTextMax = MY_LIMIT;<br />c_MyTree.GetItem(&amp;tvi);<br />s.ReleaseBuffer();</pre><p>　　可以看出来，其实上面的代码对所有类型的 Set 方法都适用，但是并不需要这么做，因为所有的类 Set 方法（包括 
      Insert方法）不会改变字符串的内容。但是当你需要写 CString 对象时，必须保证缓冲是可写的，这正是 GetBuffer 所做的事情。再次强调：
      一旦做了一次 GetBuffer 调用，那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 5、<b><a name="CString 型转化成 BSTR 型">CString 型转化成 BSTR 型</a></b><br /><br />
      　　当我们使用 ActiveX 控件编程时，经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串，Intel平台上的宽字符串（Unicode），并且
      可以包含嵌入的 NULL 字符。<br /><br />
      你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR：</p><pre>CString s;<br />s = ... ; // whatever<br />BSTR b = s.AllocSysString();</pre><p>　　现在指针 b 指向的就是一个新分配的 BSTR 对象，该对象是 CString 的一个拷贝，包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 
      的接口。通常，BSTR 由接收它的组件来释放，如果你需要自己释放 BSTR 的话，可以这么做：</p><pre>::SysFreeString(b);</pre><p>　　对于如何表示传递给 ActiveX 控件的字符串，在微软内部曾一度争论不休，最后 Visual 
      Basic 的人占了上风，BSTR（“Basic String”的首字母缩写）就是这场争论的结果。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 6、<b><a name="BSTR 型转化成 CString 型">BSTR 型转化成 CString 型</a></b><br /><br />
      　　由于 BSTR 是记数 Unicode 字符串，你可以用标准转换方法来创建 8 位的 CString。实际上，这是 CString 内建的功能。在 CString 中
      有特殊的构造函数可以把 ANSI 转化成 Unicode，也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串，VARIANT 类型是
      由各种 COM 和 Automation (自动化)调用返回的类型。<br /><br />
      例如，在一个ANSI程序中：</p><pre>BSTR b;<br />b = ...; // whatever<br />CString s(b == NULL ? L"" : b)</pre><p>　　对于单个的 BSTR 串来说，这种用法可以工作得很好，这是因为 CString 有一个特殊的构造函数以LPCWSTR（BSTR正是这种类型）
      为参数，并将它转化成 ANSI 类型。专门检查是必须的，因为 BSTR 可能为空值，而 CString 的构造函数对于 NULL 
      值情况考虑的不是很周到，（感谢 Brian Ross 指出这一点!）。这种用法也只能处理包含 NUL 终结字符的单字符串；如果要转化含有多个 NULL 字符
      串，你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意，应该尽量避免。<br />
      　　根据 C/C++ 规则，如果你有一个 LPWSTR，那么它别无选择，只能和 LPCWSTR 参数匹配。<br /><br />
      在 Unicode 模式下，它的构造函数是：</p><pre>CString::CString(LPCTSTR);</pre><p>正如上面所表示的，在 ANSI 模式下，它有一个特殊的构造函数：</p><pre>CString::CString(LPCWSTR); </pre><p>
      　　它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。（在Unicode模式下，有一个专门的构造函数，该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串
      指针，该函数将它加宽为 Unicode 的字符串！）再次强调：一定要检查 BSTR 的值是否为 NULL。<br />
      　　另外还有一个问题，正如上文提到的：BSTRs可以含有多个内嵌的NULL字符，但是 CString 的构造函数只能处理某个串中单个 NULL 字符。
      也就是说，如果串中含有嵌入的 NUL字节，CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数，你会发现
      它们都调用了lstrlen，也就是计算字符串的长度。<br />
      　　注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte，如果你不想使用这种默认的转换方式，则必须编写自己的转化代码。<br />
      　　如果你在 UNICODE 模式下编译代码，你可以简单地写成：<br /></p><pre>CString convert(BSTR b)<br />{<br />    if(b == NULL)<br />        return CString(_T(""));<br />    CString s(b); // in UNICODE mode<br />    return s;<br />}<br /></pre>
      　　如果是 ANSI 模式，则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你
      只能在想要改变这些参数进行转换时使用该技术。例如，指定不同的默认字符，不同的标志集等。
<pre>CString convert(BSTR b)<br />{<br />    CString s;<br />    if(b == NULL)<br />       return s; // empty for NULL BSTR<br />#ifdef UNICODE<br />    s = b;<br />#else<br />    LPSTR p = s.GetBuffer(SysStringLen(b) + 1); <br />    ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page<br />                          0,                 // no flags<br />                          b,                 // source widechar string<br />                          -1,                // assume NUL-terminated<br />                          p,                 // target buffer<br />                          SysStringLen(b)+1, // target buffer length<br />                          NULL,              // use system default char<br />                          NULL);             // don''t care if default used<br />    s.ReleaseBuffer();<br />#endif<br />    return s;<br />}<br /></pre>
      　　我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么，因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。

      <br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 7、<b><a name="VARIANT 型转化成 CString 型">VARIANT 型转化成 CString 型</a></b><br /><br />
      　　事实上，我从来没有这么做过，因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert 
      Quirk 的一篇帖子谈到了这种转化，我觉得把他的文章包含在我的文章里是不太好的做法，所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。<br />
      　　VARIANT 类型经常用来给 COM 对象传递参数，或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法，函数返回什么类型
      依赖可能（并且常常）方法的输入参数（比如，在自动化操作中，依赖与你调用哪个方法。IDispatch::Invoke 可能返回（通过其一个参数）一个
      包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果，（详见 MSDN 上的 VARIANT 结构的定义）。在下面的例子中，假设
      类型是一个BSTR的变体，也就是说在串中的值是通过 bsrtVal 来引用，其优点是在 ANSI 应用中，有一个构造函数会把 LPCWCHAR 
      引用的值转换为一个 CString（见 BSTR-to-CString 部分）。在 Unicode 模式中，将成为标准的 CString 
      构造函数，参见对缺省::WideCharToMultiByte 转换的告诫，以及你觉得是否可以接受（大多数情况下，你会满意的）。<pre>VARIANT vaData;<br />vaData = m_com.YourMethodHere();<br />ASSERT(vaData.vt == VT_BSTR);<br />CString strData(vaData.bstrVal);</pre><p>你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑：<br /></p><pre>CString VariantToString(VARIANT * va)<br />{<br />    CString s;<br />    switch(va-&gt;vt)<br />      { /* vt */<br />       case VT_BSTR:<br />          return CString(vaData-&gt;bstrVal);<br />       case VT_BSTR | VT_BYREF:<br />          return CString(*vaData-&gt;pbstrVal);<br />       case VT_I4:<br />          s.Format(_T("%d"), va-&gt;lVal);<br />          return s;<br />       case VT_I4 | VT_BYREF:<br />          s.Format(_T("%d"), *va-&gt;plVal);<br />       case VT_R8:<br />          s.Format(_T("%f"), va-&gt;dblVal);<br />          return s;<br />       ... 剩下的类型转换由读者自己完成<br />       default:<br />          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)<br />          return CString("");<br />      } /* vt */<br />}<br /></pre><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 8、<b><a name="载入字符串表资源">载入字符串表资源</a></b><br /><br />
      　　如果你想创建一个容易进行语言版本移植的应用程序，你就不能在你的源代码中直接包含本土语言字符串
      （下面这些例子我用的语言都是英语，因为我的本土语是英语），比如下面这种写法就很糟：<pre>CString s = "There is an error";</pre><p>　　你应该把你所有特定语言的字符串单独摆放（调试信息、在发布版本中不出现的信息除外）。这意味着向下面这样写比较好：</p><pre>s.Format(_T("%d - %s"), code, text);</pre><p>　　在你的程序中，文字字符串不是语言敏感的。不管怎样，你必须很小心，不要使用下面这样的串：</p><pre>// fmt is "Error in %s file %s"<br />// readorwrite is "reading" or "writing"<br />s.Format(fmt, readorwrite, filename); </pre><p>
　　这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误，尽管我懂德语，知道在德语的语法中动词放在句子的最后面，我们的德国方面的发行人
还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法（也是我现在使用的办法）是使用两个字符
串，一个用
于读，一个用于写，在使用时加载合适的版本，使得它们对字符串参数是非敏感的。也就是说加载整个格式，而不是加载串
“reading”，“writing”：</p><pre>// fmt is "Error in reading file %s"<br />// "Error in writing file %s"<br />s.Format(fmt, filename);</pre><p>
      　　一定要注意，如果你有好几个地方需要替换，你一定要保证替换后句子的结构不会出现问题，比如在英语中，可以是主语-宾语，主语-谓语，动词-宾语的结构等等。<br />
　　在这里，我们并不讨论 FormatMessage，其实它比 sprintf/Format 还要有优势，但是不太容易和CString
结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字，这样在你输出的时候就不会把他们的位置排错了。<br />
　　接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE
的段中。过程如下：首先使用 Visual Studio 的资源编辑器创建一个字符串，然后给每一个字符串取一个ID，一般我们给它取名字都以
IDS_开头。所以如果你有一个信息，你可以创建一个字符串资源然后取名为 IDS_READING_FILE，另外一个就取名为
IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中：</p><pre>STRINGTABLE<br />IDS_READING_FILE "Reading file %s"<br />IDS_WRITING_FILE "Writing file %s"<br />END</pre><p><b>注意：</b>这些资源都以 Unicode 的格式保存，不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在，虽然 Win9x 不能真正处理 Unicode。<br />
      然后你可以这样使用这些资源：<br />
      // 在使用资源串表之前，程序是这样写的：<br /></p><pre>   CString fmt;<br />      if(...)<br />        fmt = "Reading file %s";<br />     else<br />       fmt = "Writing file %s";<br />  ...<br />    // much later<br />  CString s;<br />  s.Format(fmt, filename); <br /></pre>

// 使用资源串表之后，程序这样写：
<pre>    CString fmt;<br />        if(...)<br />           fmt.LoadString(IDS_READING_FILE);<br />        else<br />           fmt.LoadString(DS_WRITING_FILE);<br />    ...<br />      // much later<br />    CString s;<br />    s.Format(fmt, filename);<br /></pre>
      　　现在，你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数，然后它从 STRINGTABLE 中取出它对应的字符串，赋值给 CString 对象。

CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出，但是在
      构造函数的示例程序中使用了。（为什么这个特性没有成为正式文档的一部分，而是放在了一个例子中，我记不得了！）——【<b>译者注</b>：从这句话看，作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查
      】。这个特征就是：如果你将一个字符串资源的ID强制类型转换为 LPCTSTR，将会隐含调用 LoadString。因此，下面两个构造字符串的例子具有相同的效果，而且其 ASSERT 在debug模式下不会被触发：<pre>CString s;<br />s.LoadString(IDS_WHATEVER);<br />CString t( (LPCTSTR)IDS_WHATEVER );<br />ASSERT(s == t);//不会被触发，说明s和t是相同的。</pre><p>　
　现在，你可能会想：这怎么可能工作呢？我们怎么能把 STRINGTABLE ID 转化成一个指针呢？很简单：所有的字符串 ID
都在1~65535这个范围内，也就是说，它所有的高位都是0，而我们在程序中所使用的指针是不可能小于65535的，因为程序的低 64K
内存永远也不可能存在的，如果你试图访问0x00000000到0x0000FFFF之间的内存，将会引发一个内存越界错误。所以说1~65535的值不
可能是一个内存地址，所以我们可以用这些值来作为字符串资源的ID。<br />
　　我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC
中使用的标准宏。你要记住，大多数的方法即可以接受一个 UINT 型的参数，也可以接受一个 LPCTSTR 型的参数，这是依赖 C++
的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样，你也可以给很多种结构只传递一个资源名。</p><pre>CString s;<br />s.LoadString(IDS_WHATEVER);<br />CString t( MAKEINTRESOURCE(IDS_WHATEVER));<br />ASSERT(s == t);</pre><p>
      　　告诉你吧：我不仅只是在这里鼓吹，事实上我也是这么做的。在我的代码中，你几乎不可能找到一个字符串，当然，那些只是偶然在调试中出现的或者和语言无关的字符串除外。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 9、<b><a name="CString 和临时对象">CString 和临时对象</a></b><br /><br />
      　　这是出现在 microsoft.public.vc.mfc 
      新闻组中的一个小问题，我简单的提一下，这个问题是有个程序员需要往注册表中写入一个字符串，他写道：<br />
　　我试着用 RegSetValueEx()
设置一个注册表键的值，但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作，但是当我用 CString
的时候，总是得到一些垃
圾："&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;...&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;&amp;Yacute;"
为了确认是不是我的 CString 数据出了问题，我试着用 GetBuffer，然后强制转化成 char*，LPCSTR。GetBuffer
返回的值是正确的，但是当我把它赋值给 char* 时，它就变成垃圾了。以下是我的程序段：</p><pre>char* szName = GetName().GetBuffer(20);<br />RegSetValueEx(hKey, "Name", 0, REG_SZ, <br />             (CONST BYTE *) szName,<br />             strlen (szName + 1));</pre><p>这个 Name 字符串的长度小于 20，所以我不认为是 GetBuffer 的参数的问题。<br /><br />
      真让人困惑，请帮帮我。<br /><br />
      亲爱的 Frustrated，<br /><br />
      你犯了一个相当微妙的错误，聪明反被聪明误，正确的代码应该象下面这样：<br /></p><pre>CString Name = GetName();<br />RegSetValueEx(hKey, _T("Name"), 0, REG_SZ, <br />                    (CONST BYTE *) (LPCTSTR)Name,<br />                    (Name.GetLength() + 1) * sizeof(TCHAR));<br /></pre>
      　　为什么我写的代码能行而你写的就有问题呢？主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见：《C++ 
      Reference manual》§12.2<br />
      　　在一些环境中，编译器有必要创建一个临时对象，这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话，编译器要确保这个类的构造函数被调用。同样的，如果这个类声明有析构函数的话，也要保证这个临时对象的析构函数被调用。<br />
      　　编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。<br />
　　大部分的编译器是这样设计的：在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数，实现起来，一般都是在下一个分号处。因此，
这个 CString 对象在 GetBuffer 调用之后就被析构了（顺便提一句，你没有理由给 GetBuffer
函数传递一个参数，而且没有使用ReleaseBuffer 也是不对的）。所以 GetBuffer
本来返回的是指向这个临时对象中字符串的地址的指针，但是当这个临时对象被析构后，这块内存就被释放了。然后 MFC
的调试内存分配器会重新为这块内存全部填上
0xDD，显示出来刚好就是“&amp;Yacute;”符号。在这个时候你向注册表中写数据，字符串的内容当然全被破坏了。<br />
　　我们不应该立即把这个临时对象转化成 char* 类型，应该先把它保存到一个 CString
对象中，这意味着把临时对象复制了一份，所以当临时的 CString 对象被析构了之后，这个 CString
对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。<br />
　　此外，我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小，使用lstrlen(Name+1) 得到的实际结果对于
Unicode 字符来说比 ANSI 字符要小一半，而且它也不能从这个字符串的第二个字符起开始计算，也许你的本意是 lstrlen(Name)
+ 1（OK，我承认，我也犯了同样的错误！）。不论如何，在 Unicode
模式下，所有的字符都是2个字节大小，我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默：REG_SZ
的值究竟是以字节计算还是以字符计算呢？我们假设它指的是以字节为单位计算，你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /> 10、<b><a name="CString的效率">CString 的效率</a></b><br /><br />
      　　CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲，它也确实可以被实现得更加高效，你可能会说下面的代码：<pre>CString s = SomeCString1;<br />s += SomeCString2;<br />s += SomeCString3;<br />s += ",";<br />s += SomeCString4;</pre><p>比起下面的代码来，效率要低多了：</p><pre>char s[1024];<br />lstrcpy(s, SomeString1);<br />lstrcat(s, SomeString2);<br />lstrcat(s, SomeString 3);<br />lstrcat(s, ",");<br />lstrcat(s, SomeString4);</pre><p>　　总之，你可能会想，首先，它为 SomeCString1 分配一块内存，然后把 SomeCString1 复制到里面，然后发现它要做一个连接，则重新分配一块新的足够大的内存，大到能够放下当前的字符串加上SomeCString2，把内容复制到这块内存
      ，然后把 SomeCString2 连接到后面，然后释放第一块内存，并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上，在很多情况下根本就不需要复制源字符串（在 += 操作符左边的字符串）。<br />
　　在 VC++6.0 中，Release 模式下，所有的 CString 中的缓存都是按预定义量子分配的。所谓量子，即确定为
64、128、256 或者 512 字节。这意味着除非字符串非常长，连接字符串的操作实际上就是 strcat
经过优化后的版本（因为它知道本地的字符串应该在什么地方结束，所以不需要寻找字符串的结尾；只需要把内存中的数据拷贝到指定的地方即可）加上重新计算字
符串的长度。所以它的执行效率和纯 C 的代码是一样的，但是它更容易写、更容易维护和更容易理解。<br />
      　　如果你还是不能确定究竟发生了怎样的过程，请看看 CString 的源代码，strcore.cpp，在你 vc98的安装目录的 mfc\src 子目录中。看看 ConcatInPlace 方法，它被在所有的 += 操作符中调用。<br /><br />
      啊哈！难道 CString 真的这么"高效"吗？比如，如果我创建</p><pre>CString cat("Mew!");</pre><p>　　然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区（4个字符加一个结束字符），系统将给我分配64个字节，而其中59个字节都被浪费了。<br />
      　　如果你也是这么想的话，那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错，这种说法的确正确，但是他忽略了事实中一个很重要的方面。<br />
      　　如果你编写的是运行在16K EPROMs下的嵌入式程序的话，你有理由尽量少使用空间，在这种环境下，它能使你的程序更健壮。但是在 500MHz, 
      256MB的机器上写 Windows 程序，如果你还是这么做，它只会比你认为的“低效”的代码运行得更糟。<br />
　　举例来说。字符串的大小被认为是影响效率的首要因素，使字符串尽可能小可以提高效率，反之则降低效率，这是大家一贯的想法。但是这种想法是不对的，精
确的内存分配的后果要在程序运行了好几个小时后才能体现得出来，那时，程序的堆中将充满小片的内存，它们太小以至于不能用来做任何事，但是他们增加了你程
序的内存用量，增加了内存页面交换的次数，当页面交换的次数增加到系统能够忍受的上限，系统则会为你的程序分配更多的页面，直到你的程序占用了所有的可用
内存。由此可见，虽然内存碎片是决定效率的次要因素，但正是这些因素实际控制了系统的行为，最终，它损害了系统的可靠性，这是令人无法接受的。<br />
      　　记住，在 debug 模式下，内存往往是精确分配的，这是为了更好的排错。<br />
      　　假设你的应用程序通常需要连续工作好几个月。比如，我常打开 VC++，Word，PowerPoint，Frontpage，Outlook 
      Express，Forté Agent，Internet 
      Explorer和其它的一些程序，而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天（反之，如果你不幸不得不使用像 Adobe 
      FrameMaker 这样的程序的话，你将会体会到可靠性的重要；这个程序机会每天都要崩溃4~6次，每次都是因为用完了所有的空间并填满我所有的交换页面）。所以精确内存分配是不可取的，它会危及到系统的可靠性，并引起应用程序崩溃。<br />
　　按量子的倍数为字符串分配内存，内存分配器就可以回收用过的内存块，通常这些回收的内存块马上就可以被其它的 CString
对象重新用到，这样就可以保证碎片最少。分配器的功能加强了，应用程序用到的内存就能尽可能保持最小，这样的程序就可以运行几个星期或几个月而不出现问
题。<br />
　　题外话：很多年以前，我们在 CMU 写一个交互式系统的时候，一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim
Mitchell，现在他在 Sun Microsystems
工作，那时侯他创造了一种内存分配器，它保留了一个内存分配状况的运行时统计表，这种技术和当时的主流分配器所用的技术都不同，且较为领先。当一个内存块
需要被分割得比某一个值小的话，他并不分割它，因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针，他认为：
与其让指令做长时间的存取内存操作，还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。（His observation was that
the long-term saving in instructions by not having to ignore unusable
small storage chunks far and away exceeded the additional cost of doing
a few floating point operations on an allocation operation.）他是对的。<br />
      　　永远不要认为所谓的“最优化”是建立在每一行代码都高速且节省内存的基础上的，事实上，高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上，只使用最小内存的字符串分配策略可能是最糟糕的一种方法。<br />
      　　如果你认为优化是你在每一行代码上做的那些努力的话，你应该想一想：在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your 
      Worst Enemy for some thought-provoking ideas》。<br />
      　　记住，+= 运算符只是一种特例，如果你写成下面这样：</p><pre>CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;</pre>
      则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。<br /><br /><img src="http://www.vckbase.com/document/image/paragraph.gif" /><b><a name="总结">总结</a></b><br /><br />
      　　以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类，但是 MFC 没有很明显的指出这些特征，需要你自己去探索、去发现。<img src ="http://www.blogjava.net/tingfeng/aggbug/261265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/tingfeng/" target="_blank">听风</a> 2009-03-21 22:49 <a href="http://www.blogjava.net/tingfeng/articles/261265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>