??xml version="1.0" encoding="utf-8" standalone="yes"?> //Author : cppgp
1 . int _access( const char *path, int mode ); q回?: 拥有该权限返?
2 . int _chdir( const char *dirname );
3 . int _chdrive( int drive );
4 . int _findclose( long handle );
5 . long _findfirst( char *filespec, struct _finddata_t *fileinfo );
6 . int _findnext( long handle, struct _finddata_t *fileinfo );
7 . char *_getcwd( char *buffer, int maxlen );
8 . char *_getdcwd( int drive, char *buffer, int maxlen );
9 . int _getdrive( void );
10 . unsigned long _getdrives(void);
11 . int _mkdir( const char *dirname );
12 . int _rmdir( const char *dirname ); //--end-- //如有错误Q希望指? //联系 : cppgp@163.com
]]>
]]>
//Email : cppgp@163.com
//Time : 2007 03 30
//按照字母序排列
功 ??: 定文g/目录存取权限.
头文?: #include <io.h>
参 ??: path:文g或者目?br />
mode:权限讑֮,其值如?
00 Existence only
02 Write permission
04 Read permission
06 Read and write permission
没有权限q回-1Q且讄errno为如下?br />
ENOENT 路径/文g不存?br />
EACCES 没有相应权限
功 ??: 更改当前工作目录.
头文?: #include <direct.h>
q回?: 成功q回0
p|q回-1Q且讄errno如下:
ENOENT 该\径不存在
功 ??: 更改当前工作驱动?
头文?: #include <direct.h>
q回?: 成功q回0
p|q回-1
注 ??: 参数说明
drive =1<==> A?br />
drive =2<==> B?br />
drive =3<==> C?br />
如此{等,该函数可以由_chdir代替
功 ??: 关闭搜寻句柄q攄应资?br />
头文?: #include <io.h>
参 ??: long handle 搜烦句柄(通常q靠其前的_findfirst()q回,_findfirst()见下)
fileinfo 文g信息buffer
q回?: 成功q回0
出错q回-1,且设|errno为如下?br />
ENOENT 没有更多的符合该泛式的文?/p>
功 ??: 提供与filespec指定入口泛式匚w的第一个文?通常后用_findnext函数后箋使用来完成某泛式下的文g遍历.
头文?: #include <io.h>
参 ??: filespec 目标文g规范,可以包含通配W?br />
fileinfo 文g信息buffer
q回?: 成功q回唯一的搜索句?br />
出错q回-1,且设|errno为如下?br />
ENOENT 该泛式无法匹?br />
EINVAL 无效文g?/p>
功 ??: 按照前面_findfirst中的泛式规则Q查找下一个符合该泛式的文Ӟq以 此ؓ依据修改fileinfo中的?br />
头文?: #include <io.h>
参 ??: long handle 搜烦句柄(通常q靠其前的_findfirst()q回)
fileinfo 文g信息buffer
q回?: 成功q回0
出错q回-1,且设|errno为如下?br />
ENOENT 没有更多的符合该泛式的文?/p>
功 ??: 获得当前工作目录.
头文?: #include <direct.h>
q回?: 成功q回指向buffer的pointer
p|q回NULLQ且讄errnoZ下三个g一:
ENODEV 无该讑֤
ENOMEM 内存不够
ERANGE l果出范围
注 ??: 当第一个参Cؓ NULL ?W二个参?maxlen 长度讄无效,且函C用malloc分配_内存,需要将函数q回g递给free()函数来释攑ֆ?
当第一个参C?NULL ?maxlen 指定长度不够函数q回?讄errno为ERANGE
功 ??: 获得指定驱动器的当前工作路径.
头文?: #include <direct.h>
q回?: 成功q回指向buffer的pointer
p|q回NULLQ且讄errnoZ下三个g一:
ENODEV 无该讑֤
ENOMEM 内存不够
ERANGE l果出范围
注 ??: 当第一个参Cؓ NULL ?该函数设|errno为ERANGE
功 ??: 获得当前盘驱动?
头文?: #include <direct.h>
q回?: q回驱动器?1<==>A 2<==>B 如此{等;函数不会出错!
功 ??: 获得当前所有驱动器.
头文?: #include <direct.h>
q回?: 各个位代表对应驱动器,
bit 0 <==> A
bit 1 <==> B
bit 2 <==> C
... ...
注:bit x 表示unsigned long的第x?/p>
功 ??: 创徏一个新目录,目录名ؓdirname.
头文?: #include <direct.h>
q回?: 成功q回0
p|q回-1Q且讄errnoZ下三个g一
EACCESS 权限不允?br />
EEXIST 该目录已存在
ENOENT 无该文g或目?/p>
功 ??: 删除名ؓdirname的目?
头文?: #include <direct.h>
q回?: 成功q回0
p|q回-1Q且讄errnoZ下三个g一
EACCESS 权限不允?br />
ENOTEMPTY dirname不是文g?
或者该文g夹不I?
或者dirname为当前工作文件夹;
或者dirname为当Ҏ件夹;
ENOENT 无该文g或目?/p>
]]>
Ҏ册表的编E要用到句柄Q我们需要通过一个句柄访问注册表键|当打开或创Z个键值的时候,会返回一个该键的句柄Qƈ且调用和分析键和创徏键|
在分析和创徏的同旉要传递句柄到函数。WINDOWS提供预定义的用语---根一U键的保留句柄,?
HKEY_CLASS_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USER{,q些都是与注册表
的根键相对应q且同名的句柄。当讉K一个根键的时候,传递这些通用句柄。这׃用打开栚w啦,因ؓ他们L在打开状态下Q可使用默认键的句柄讉K?br />
Win32
API提供了大U?5个有x册表的函敎ͼ他提供了Ҏ册表的读取,写入Q删除,以及打开注册表的所有函敎ͼq且可以实现Ҏ册表备䆾Q连接和对远端注?
表进行查看等功能。但是在~程的时候首先需要考虑你是在什么操作系l编辑此cȝ序,虽然微Y的操作系l,如NT和Windows98都是32位操作系l,
但是有些API函数中ƈ不支?8Q这Ҏ要注意的。APIl历和发展了很多q_有些函数已经重复Q比如RegSetValue()?
RegSetValueEx()都是用来讄注册表键值的Q两者的区别在于前者是讄注册表键的默认|仅支持作为数据类型的字符?而后者不仅承了?
者的所有功能而且q能对多值或cdq行操作。一般APIҎ较新的函数都会在后缀q加"Ex"的同样名U函敎ͼ在编E中均应可能的使用高函数。下
面介l一些比较常用的操作注册表的API函数Q?br />
1、RegCloseKey()
原型QRegCloseKey(HKEY hKey)
解释Q关闭指定的d表键Q释攑֏柄。当对一个或多个键或值操作完成以后,需要关闭其键来q行保存操作l果Q关闭一个键后,句柄变ؓ非法Q此时应释放句柄?br />
2、RegCreateKeyEx()
原型QLONG RegCreateKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD
Reserved,
LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult, LPDWORD lpdwDisposition );
解释Q打开指定的键或子键。如果要打开的键不存在的话,本函C试图建立它。提供该函数是ؓ了向后兼宏V所有的WIN32应用E序应用函数RegCreateKeyExQ)。各参数及返回值的含义如下Q?br />
各参数及q回值的含义如下Q?br />
·hKeyZ键|可以取下面的一些数|HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG、
HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USER?
HKEY_PERFORMANCE_DATA(WINNT操作pȝ)、HKEY_DYN_DATAQWIN9X操作pȝQ;
·参数lpSubKeyZ个指向以零结字符串的指针Q其中包含将要创建或打开的子键的名称。子键不可以用反斜线Q\Q开始。该参数可以为NULLQ?br />
·参数ReservedZ留|必须讄?Q?br />
·参数lpClassZ个指向包含键cd的字W串。如果该键已l存在,则忽略该参数Q?br />
·参数dwOptions为新创徏的键讄一定的属性。可以取下面的一些数| REG_OPTION_NON_VOLATILE
Q表C新创徏的键Z个非短暂性的键(数据信息保存在文件中Q当pȝ重新启动Ӟ数据信息恢复Q;REG_OPTION_VOLATILEQ表C新创徏?
键ؓ一个短暂性的键(数据信息保存在内存中Q,Windows95忽略该数|REG_OPTION_BACKUP_RESTORE
仅在WINNT中支持,可以提供优先U支持;
·参数samDesired用来讄寚w讉K的权限,可以取下面的一些数|KEY_CREATE_LINKQ表C准许生成符号键QKEY_CREATE_SUB_KEY
表示准许生成子键QKEY_ENUMERATE_SUB_KEYS
表示准许生成枚D子键QKEY_EXECUTE
表示准许q行L作;KEY_NOTIFY表示准许更换通告Q
KEY_QUERY_VALUE 表示准许查询子键QKEY_ALL_ACCESS
提供完全讉KQ是上面数值的l合Q?br />
KEY_READ
是下面数值的l合QKEY_QUERY_VALUE、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFYQ KEY_SET_VALUE
表示准许讄子键的数|KEY_WRITE
是下面数值的l合QKEY_SET_VALUE、KEY_CREATE_SUB_KEYQ?br />
·参数lpSecurityAttributesZ个指向SECURITY_ATTRIBUTESl构的指针,定q回的句柄是否被子处理过Eѝ如果该参数为NULLQ则句柄不可以被l承。在WINNT中,该参数可以ؓ新创建的键增加安全的描述Q?br />
·参数phkResultZ个指向新创徏或打开的键的句柄的指针Q?br />
·参数lpdwDispition指明键是被创是被打开的,可以是下面的一些数| REG_CREATE_NEW_KEY
表示键先前不存在Q现在被创徏QREG_OPENED_EXISTING_KEY
表示键先前已存在Q现在被打开?br />
如果该函数调用成功,则返回ERROR_SUCCESS。否则,q回gؓ文gWINERROR.h中定义的一个非零的错误代码Q可以通过讄
FORMAT_MESSAGE_FROM_SYSTEM标识调用FormatMessageQ)函数来获取一个对错误的M描述?br />
3、RegOpenKeyExQ)
原型QLONG RegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD
ulOptions,
REGSAM samDesired, PHKEY phkResult );
解释Q打开一个指定的键,q返回打开键的句柄?br />
各参数及q回值的含义如下Q?br />
·参数hKey的含义同RegCreateKeyEx函数中的hKey参数Q?br />
·参数lpSubKeyZ个指向以零结字符串的指针Q其中包含子键的名称Q可以利用反斜线Q\Q分隔不同的子键名。如果字W串为空Q则ҎhKey参数创徏一个新的句柄。在q种情况下,q不关闭先前打开的句柄;
·参数ulOption保留Q通常必须讄?Q?br />
·参数samDesired的含义同RegCreateKeyEx函数中的samDesired参数Q?br />
·参数phkResultZ个指针,用来指向打开的键的句柄。可以通过RegCloseKey函数关闭q个句柄Q?br />
·函数的返回值同RegCreateKeyEx函数的返回倹{?br />
4?查询某一个键|RegQueryValueExQ)
原型QLONG RegQueryValueEx(HKEY hKey, LPCTSTR lpValueName,
LPDWORD pReserved, LPDWORD lpType,
LPBYTE lpData, LPDWORD lpcbData );
解释Q根据要查询的键的句柄,要返回的查询的数据?br />
各个参数及返回值的含义如下Q?br />
·参数hKey为当前的一个打开的键的句柄,具体数值同RegCreateKeyEx函数的hKey参数Q?br />
·参数lpVauleNameZ个指向非I的包含查询值的名称的字W串指针Q?br />
·参数lpReserved保留Q必MؓNULLQ?br />
·参数lpTypeZ个指向数据类型的指针Q数据类型ؓ下列cd之一QREG_BINARY
二进制数据、REG_DWORD 32位整数、REG_DWORD_LITTLE_ENDIAN
littleQendian格式的数据,例如0X12345678以(0X78 0X56 0X34
0X12Q方式保存、REG_DWORD_BIG_ENDIAN
bigQendian格式的数据,例如0X12345678以(0X12 0X34 0X56
0X78Q方式保存、REG_EXPAND_SZ
一个包含未扩展环境变量的字W串、REG_LINK
一个Unicodecd的链接、REG_MULIT_SZ 以两个零l尾的字W串、REG_NONE
无类型数倹{REG_RESOURCE_LIST 讑֤驱动资源列表、REG_SZ
一个以零结字符串根据函C用的字符集类型的不同而设|ؓUnicode或ANSIcd的字W串Q?br />
·参数lpDataZ个指向保存返回值的变量的指针。如果不需要返回|该参数可以ؓNULLQ?br />
·参数lpcbDataZ个指向保存返回值长度的变量的指针。其中长度以字节为单位。如果数据类型ؓREG_SZ、REG_MULTI_SZ?
REG_EXPAND_SZQ那么长度也包括l尾的零字符Q只有在参数lpData为NULLӞ参数lpcbData才可以ؓNULLQ返回值同
RegCreateKeyEx函数的返回|
5、RegSetValueExQ)
原型QLONG RegSetValueEx(HKEY hKey, LPCTSTR lpValueName,
LPDWORD lpReserved, DWORD dwType,
const BYTE *lpData, DWORD cbData);
解释Q设|注册表中的一个键倹{?br />
各个参数及返回值的含义如下Q?br />
·参数hKey的含义同RegCreateKeyEx函数中的hKey参数Q?br />
·参数lpValueNameZ个指向包含值名的字W串指针QReserved保留Q通常必须讄?Q?br />
·参数dwType定了设|的值的cd同RegQueryValueKeyEx的lyType参数Q?br />
·参数lpDataZ个指向包含数据的~冲区的指针Q?br />
·参数cbData以字节ؓ单位Q指定数据的长度Q?br />
q回值同RegCreateKeyEx函数的返回倹{?br />
6、RegDeketeKeyQ)
原型QLONG RegDeleteKeyQHKEY hKeyQLPCTSTR lpSubKEYQ;
解释Q函数RegDeketeKey删除一个键及所有的子键?br />
各个参数及返回值的含义如下Q?br />
·参数hKey的含义同RegCreateKeyEx函数中的hKey参数Q?br />
·参数lpSubKey的含义同RegCreateKeyEx函数中的lpSubKey参数。
]]>
1Q“启动”文件夹──最常见的自启动E序文g夏V?br />
它位于系l分区的“documents and SettingsQ->UserQ->〔开始〕菜单-Q?gt;E序”目录下。这时的User指的是登录的用户名?br />
2Q“All Users”中的自启动E序文g夹──另一个常见的自启动程序文件夹?br />
它位于系l分区的“documents and SettingsQ->All
UserQ->〔开始〕菜单-Q?gt;E序”目录下。前面提到的“启动”文件夹q行的是d用户的自启动E序Q而“All
Users”中启动的程序是在所有用户下都有效(不论你用什么用L录)?br />
3Q“Load”键值── 一个埋藏得较深的注册表键倹{?br />
位于〔HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\load〕主键下?br />
4Q“Userinit”键值──用户相关
它则位于〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows
NT\CurrentVersion\Winlogon\Userinit〕主键下Q也是用于系l启动时加蝲E序的。一般情况下Q其默认gؓ
“userinit.exe”,׃该子键的g可用逗号分隔开多个E序Q因此,在键值的数g可加入其它程序?br />
5Q“Explorer\Run”键值──与“load”和“Userinit”两个键g同的是,“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 />
6Q“RunServicesOnce”子键──它在用户d前及其它注册表自启动E序加蝲前面加蝲?br />
q个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunServicesOnce〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunServicesOnce〕下?br />
7Q“RunServices”子键──它也是在用户d前及其它注册表自启动E序加蝲前面加蝲?br />
q个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunServices〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunServices〕下?br />
8Q“RunOnce\Setup”子键──光认值是在用L录后加蝲的程序?br />
q个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunOnce\Setup〕和〔HKEY_LOCAL_MACHINE\Software
\Microsoft\Windows\CurrentVersion\RunOnce\Setup〕下?br />
9Q“RunOnce”子键──许多自启动程序要通过RunOnce子键来完成第一ơ加载?br />
q个键同时位于〔HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\RunOnce〕和〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows\CurrentVersion\RunOnce〕下。位于〔HKEY_CURRENT_USER〕根键下的RunOnce子键在用L
录扣及其它注册表的Run键值加载程序前加蝲相关E序Q而位于〔HKEY_LOCAL_MACHINE〕主键下的Runonce子键则是在操作系l处理完
其它注册表Run子键及自启动文g夹内的程序后再加载的。在Windows
XP中还多出一个〔HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion
\RunOnceEX〕子键,光理相同?br />
10Q“Run”子键──目前最常见的自启动E序用于加蝲的地斏V?br />
q个键同时位于〔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 />
11Q再者就是Windows中加载的服务了,它的U别较高Q用于最先加载?br />
其位于〔HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services〕下Q看C吗,你所有的pȝ服务加蝲E序都在q里了!
12QWindows Shell──pȝ接口
它位于〔HKEY_LOCAL_MACHINE\Software\Microsoft
\Windows
NT\CurrentVersion\Winlogon\〕下面的Shell字符串类型键gQ基默认gؓExplorer.exeQ当然可能木马程序会
在此加入自nq以木马参数的Ş式调用资源管理器Q以辑ֈƺ骗用户的目的?br />
13QBootExecute──属于启动执行的一个项?
可以通过它来实现启动NatviceE序QNativeE序在驱动程序和pȝ核心加蝲后将被加载,此时会话理?smss.exe)q行windowsNT用户模式q开始按序启动nativeE序
它位于注册表中〔HKEY_LOCAL_MACHINE\System
\ControlSet001\Session
Manager\〕下面,有一个名为BootExecute的多字符串值键Q它的默认值是"autocheck autochk
*"Q用于系l启动时的某些自动检查。这个启动项目里的程序是在系l图形界面完成前p执行的,所以具有很高的优先U?br />
14Q策略组加蝲E序──打开Gpedit.mscQ展开“用户配|——管理模李쀔—系l——登录”,可以看到“在用户d时运行这些程序”的目Q你可以在里面添加?br />
在注册表中[HKEY_CURRENT_USER\Software\Microsoft
\Windows\CurrentVersion\Group Policy
Objects\本地User\Software\Microsoft\Windows\CurrentVersion\Policies
\Explorer\Run]你也可以看到相对应的键倹{?br />
备注Q?br />
Home版的XP中没有提供gpedit工具Q可到网上搜索ƈ下蝲补丁?br />
快速进入启动项
快速进入启动项的方法是在运行中输入 msconfig Q即可看到窗口下的启动项q行目?br />
[~辑本段]从系l的启动可以看C?br />
俗话说“万事开头难”, 俗话也说“良好的开头是成功的一半”,那么XPpȝ“开头”也是“启动”能告诉我们什么那?br />
1、msconfig
?开始?“运行”对话框中输入“msconfig”就打开“系l配|实用程序”?br />
msconfig是Windowspȝ中的“系l配|实用程序”,它可以自动执行诊断xppȝ
的配|问题时所用的常规解决步骤。它的斚w可够宽,包括:一般(常规Q、system.ini、win.ini、BOOT.INI、服务、启动。它?
xppȝ底层最先启动的E序Q可见它的重要性了。这里面可是自启动程序非常喜Ƣ呆的地斏V?br />
q里我们只介l一下“启动?br />
pȝ配置实用E序中的“启动”选项和我们在下面讲的"启动"文g夹ƈ不是同一个东西,在系l配|实用程序中的这个启动项目是Windowspȝ启动目的集合地Q几乎所有的启动目部能在这里找?---当然Q经q特D编E处理的E序可以通过另外的方法不在这里显C?br />
打开“启动”标{,“启动项目”中|列的是开机启动程序的名称Q“命令”下是具体的E序附加命oQ最后的"位置"是该程序在注册表中的相应位|了Q你可以对可疑的E序q行详细的\径、命令检查,一旦发现错误,可以用下方?用"来禁止该E序开机时候的加蝲?br />
一般来讲所有我们可见的E序的列表,你完全可以通过它来理你的启动E序Q换句话Q这里可以全部是I的?br />
2、注册表中相应的启动加蝲目
注册表的启动目是病毒和木马E序的最爱,非常多的病毒木马的顽固性就是通过注册表来实现的,特别是在安装了新的Y件程序,一定不要被E序漂亮的外表迷惑,一定要看清楚它的实质是不是木马的伪装外x者是捆绑E序Q必要的时候可以根据备份来恢复注册表?br />
我们也可以通过手动的方法来查注册表中相应的位置Q注意同安全、清z的pȝ注册表相应键q行比较Q如果发C一致的地方Q一定要弄清楚它是什么东?不要怿写在外面?“system”?br />
“windows”、“programfiles”等名称Q尤其是如果你仔l观察的话,有些字符是不一LQ比?和o的区别,1和l的区别等Q如果经q详l的比较Q可以确定它是不明程序的话,不要手YQ马上删除?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的“开始”中有自带的启动文g夹,它是最常见的启动项目。如果在安装E序时设|成开机既启动Q这个程序就装入到这个文件夹中,pȝ启动׃自动地加载相应程序?br />
具体的位|是“开始”菜单中的“所有程序?“启动”选项?br />
在硬盘上的位|是QC:\Documents and Settings\你的用户名\「开始」菜单\E序\启动?
在注册表中的位置是:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run?br />
q里最好ؓI,而且用户要不时地查一下这里有什么不明的东西?
4、boot.ini
当用L电脑有ghost备䆾、dos工具或者是双系l时Q在开机后出C让用户选择Q如果不选择׃默认的启动的H口Q?屏幕底部是F8高启动Q,boot.ini控制这个地斏V?br />
里边的内容一般是
timeout=x Qx一般在1-5可以了Q?br />
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=optin /fastdetect………?
BOOT.INI是一个非帔R要的pȝ文gQ是pȝ启动Ӟ需要查询的一个系l文Ӟ它告诉启
动程序本计算机有几个操作pȝ、各pȝ的位|在哪里{信息。没有它或者误删了Q系l还能进行引|但是一个是只能引导默认的系l,不在有你的备份系l的?
导选择Q在一个是每次开机重启时都显CZ行字Q“boot.ini文g是非法的Q现在正从C:/Windows/下启动”,但是速度明显慢了。所以我们^
旉了要对其作必要的备䆾之外Q还要编辑它的方法。特别是在安装多pȝӞ如果没有按照从低到高(Windows 98、Windows
2000、Windows XP、Windows
2003)的安装顺序,该文件往往会被损坏。如果我们掌握修改和~辑它的办法Q就不会到时候无计可施了?br />
5、非法关Z后的“启动?br />
怿很多人都到q电脑开机后出现
“Checking file system on E:
The type of the file system is NTFS……然后是一些数字的变化Q最后一行是cM的??Q问号代表数字) allocation units available on disk”,然后p入系l桌面了”的情况吧?br />
q就是非正常x,如断c按热启动键启动、或强制按电源键x在开机造成的?br />
׃x的时候E盘里面的E序q在q行Q每ơ开机硬盘都会自动自Q消除错误信息等Q而如果非正常xq些E序没有正常退出,那么下次在开机电脑就要从新执行自Q以便消除消除错误信息,正常的电脑有一ơ就好了Q下ơ启动就不会出现q种情况了?br />
如果每次开机都出现q样的情冉|2个可能:一个是盘出现坏道了,盘在工作时H然关闭甉|Q?
可能会导致磁头与盘片猛烈擦而损坏硬盘,q会使磁头不能正复位而造成盘的划伤,从而在盘留下了坏道,但是电脑q能勉强使用Q出现这U情况一般只?
更换盘了。一个是盘没有问题Q但是留下了记忆的信息,l果每次都自Q消除的办法是Q开?q行中输入chkdsk E: /x/f
回RQ然后就出现个自动运行的dosH口Q等他运行完毕就没有问题了?br />
chkdsk E: /x /f的意思是Windows发现在E盘里文gpȝ有问?Q运行CHKDSK <使用选项/x /f> 来更正这些问??br />
对于FAT文gpȝQ可以用win自己的磁盘修复来操作Q方法是Q右M要操作的盘符Q属?工具Q选择查错Q把自动修复错误的勾打上Q点d始就可以了?br />
6、其他情늚“启动?br />
当用h开甉|开兛_从“启动”到q入桌面以及可以d|络后,如果p的时间很长,而且?
开、关闭、拖动一个程序文件的时候显得拖拖拉拉的Q有时候还有莫名其妙的从“启动”,q样的“启动”往往说明Q(1Q加载的启动目q多Q(2Q电脑中?
了,(3)pȝ盘的I间不了,Q?Q机p打扫一下了Q(5Q有关的g性能Ơ佳了…?br />
]]>
?? 重定位流上的文g指针
?? int fseek(FILE *stream, long offset, int fromwhere);
?q? 函数讄文g指针stream的位|。如果执行成功,stream指向以fromwhere为基准,偏移offset个字节的位置。如果执行失?比如offset过文g自n大小)Q则不改变stream指向的位|?br />
q回? 成功Q返?Q否则返回其他倹{?br />
E序?
#include <stdio.h>
long filesize(FILE *stream);
int main(void)
{
FILE *stream;
stream = fopen("MYFILE.TXT", "w+");
fprintf(stream, "This is a test");
printf("Filesize of MYFILE.TXT is %ld bytes\n", filesize(stream));
fclose(stream);
return 0;
}
long filesize(FILE *stream)
{
long curpos, length;
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
return length;
}
int fseek( FILE *stream, long offset, int origin );
W一个参数stream为文件指?br />
W二个参数offset为偏U量Q整数表C正向偏U,负数表示负向偏移
W三个参数origin讑֮从文件的哪里开始偏U?可能取gؓQSEEK_CUR?SEEK_END ?SEEK_SET
SEEK_CURQ?当前位置
SEEK_ENDQ?文gl尾
SEEK_SETQ?文g开?br />
其中SEEK_CURQSEEK_END和SEEK_SET依次?Q???br />fread
C语言库函数名: fread
?? 从一个流中读数据
函数原型: int fread(void *ptr, int size, int nitems, FILE *stream);
?敎ͼ用于接收数据的地址Q字W型指针Q(ptrQ?
单个元素的大(sizeQ?
元素个数QnitemsQ?br />
提供数据的文件指针(streamQ?br />
q回|成功d的元素个?br />
E序?
#include <string.h>
#include <stdio.h>
int main(void)
{
FILE *stream;
char msg[] = "this is a test";
char buf[20];
if ((stream = fopen("DUMMY.FIL", "w+"))
== NULL)
{
fprintf(stderr,
"Cannot open output file.\n");
return 1;
}
/* write some data to the file */
fwrite(msg, strlen(msg)+1, 1, stream);
/* seek to the beginning of the file */
fseek(stream, 0, SEEK_SET);
/* read the data and display it */
fread(buf, strlen(msg)+1, 1,stream);
printf("%s\n", buf);
fclose(stream);
return 0;
}
]]>
FILE *fopen(char *filename, *type);
fopen()函数中第一个Ş式参数表C文件名, 可以包含路径和文件名两部分。如:"B:TEST.DAT" "C:\\TC\\TEST.DAT"
注意Q如果将路径写成"C:\TC\TEST.DAT"是不正确? q一点要特别注意。fopen函数用来打开一个文Ӟ其调用的一般Ş式ؓQ?文g指针?fopen(文g名,使用文g方式)其中Q“文件指针名”必L被说明ؓFILE cd的指针变量,“文件名”是被打开文g的文件名。“用文件方式”是指文件的cd和操作要求。“文件名”是字符串常量或字符串数l。例如:
FILE *fpQ?br />fp=("file a","r");
其意义是在当前目录下打开文gfile aQ?只允许进行“读”操作,qfp指向该文件?br />又如Q?br />FILE *fphzk
fphzk=("c:\\hzk16',"rb")
其意义是打开C驱动器磁盘的根目录下的文件hzk16Q?q是一个二q制文gQ只允许按二q制方式q行L作?br />两个反斜U쀜\\ ”中的第一个表C{义字W,W二个表C根目录。用文件的方式共有12U,下面l出了它们的W号和意义?br />W二个Ş式参数表C打开文g的类型。关于文件类型的规定参见下表?
?文g操作cd
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
字符 含义
────────────────────────────
"r" 打开文字文g只读
"w" 创徏文字文g只写
"a" 增补, 如果文g不存在则创徏一?br />"r+" 打开一个文字文件读/?br />"w+" 创徏一个文字文件读/?br />"a+" 打开或创Z个文件增?br />"b" 二进制文?可以和上面每一合?
"t" 文这文g(默认?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
文g使用方式 ??br />“rt” 只读打开一个文本文Ӟ只允许读数据
“wt” 只写打开或徏立一个文本文Ӟ只允许写数据
“at” q加打开一个文本文Ӟq在文g末尾写数?br />“rb” 只读打开一个二q制文gQ只允许L?br />“wb” 只写打开或徏立一个二q制文gQ只允许写数?br />“ab? q加打开一个二q制文gQƈ在文件末ֆ数据
“rt+” d打开一个文本文Ӟ允许d?br />“wt+” d打开或徏立一个文本文Ӟ允许d
“at+” d打开一个文本文Ӟ允许读,或在文g末追加数 ?br />“rb+” d打开一个二q制文gQ允许读和写
“wb+” d打开或徏立一个二q制文gQ允许读和写
“ab+? d打开一个二q制文gQ允许读Q或在文件末q加数据
对于文g使用方式有以下几点说明:
1. 文g使用方式由r,w,a,t,bQ?六个字符拼成Q各字符的含义是Q?br />r(read): ?br />w(write): ?br />a(append): q加
t(text): 文本文gQ可省略不写
b(banary): 二进制文?br />+: d?br />
2. 凡用“r”打开一个文件时Q该文g必须已经存在Q?且只能从该文件读出?br />3. 用“w”打开的文件只能向该文件写入?若打开的文件不存在Q则以指定的文g名徏立该文gQ若打开的文件已l存在,则将该文件删去,重徏一个新文g?br />4. 若要向一个已存在的文件追加新的信息,只能用“a ”方式打开文g。但此时该文件必L存在的,否则会出错?br />5. 在打开一个文件时Q如果出错,fopen返回一个空指针值NULL。在E序中可以用q一信息来判别是否完成打开文g的工作,q作相应的处理。因此常用以下程序段打开文gQ?br />if((fp=fopen("c:\\hzk16","rb")==NULL)
{
printf("\nerror on open c:\\hzk16 file!");
getch();
exit(1);
}
q段E序的意义是Q如果返回的指针为空Q表CZ能打开C盘根目录下的hzk16文gQ则l出提示信息“error on open c:\ hzk16file!”,下一行getch()的功能是从键盘输入一个字W,但不在屏q上昄。在q里Q该行的作用是等待, 只有当用户从键盘敲Q一键时Q程序才l箋执行Q?因此用户可利用这个等待时间阅d错提C。敲键后执行exit(1)退出程序?br />
6. 把一个文本文件读入内存时Q要ASCII码{换成二进制码Q?而把文g以文本方式写入磁盘时Q也要把二进制码转换成ASCII码,因此文本文g的读写要p较多的{换时间。对二进制文件的d不存在这U{换?br />
7. 标准输入文g(键盘)Q标准输出文?昄?)Q标准出错输?出错信息)是由pȝ打开的,可直接用。文件关闭函敎ͽclose文件一旦用完毕,应用关闭文g函数把文件关闭, 以避免文件的数据丢失{错误?br />如果要打开一个CCDOS子目录中, 文g名ؓCLIB的二q制文g, 可写?
fopen("c:\\ccdos\\clib", "rb");
如果成功的打开一个文? fopen()函数q回文g指针, 否则q回I指?NULL)。由此可判断文g打开是否成功?br />2. fclose()函数
fclose()函数用来关闭一个由fopen()函数打开的文?, 其调用格式ؓ:
nt fclose(FILE *stream);
该函数返回一个整型数。当文g关闭成功? q回0, 否则q回一个非零倹{可以根据函数的q回值判断文件是否关闭成功?br />例子Q?br />FILE *fpOut=fopen(“c:\\a.txt?”wt+?;
Int a=1;
Fprintf(fpOut,?d?a);
Fclose(fpOut);
]]>
strcpyQ?/strong>字W串source拯到字W串destination中?br />
strcpy函数应用举例
原型Qstrcpy(char destination[], const char source[]);
功能Q将字符串source拯到字W串destination?br />
例程Q?
#include <iostream.h>
#include <string.h>
void main(void)
{
char str1[10] = { "TsinghuaOK"};
char str2[10] = { "Computer"};
cout <<strcpy(str1,str2)<<endl;
}
q行l果?Computer
W二个字W串覆盖掉W一个字W串的所有内容!
注意Q在定义数组Ӟ字符数组1的字W串长度必须大于或等于字W串2的字W串长度。不能用赋D句将一个字W串帔R或字W数l直接赋l一个字W数l。所有字W串处理函数都包含在头文件string.h中?/p>
2、strncpy(char destination[], const char source[], int numchars);
strncpyQ?/strong>字W串source中前numchars个字W拷贝到字符串destination中?br />
strncpy函数应用举例
原型Qstrncpy(char destination[], const char source[], int numchars);
功能Q将字符串source中前numchars个字W拷贝到字符串destination?br />
例程Q?
#include <iostream.h>
#include <string.h>
void main(void)
{
char str1[10] = { "Tsinghua "};
char str2[10] = { "Computer"};
cout <<strncpy(str1,str2,3)<<endl;
}
q行l果QComnghua
注意Q字W串source中前numchars个字W将覆盖掉字W串destination中前numchars个字W!
3、strcat(char target[], const char source[]);
strcat:字W串source接到字符串target的后面?br />
strcat函数应用举例
原型Qstrcat(char target[], const char source[]);
功能Q将字符串source接到字符串target的后?br />
例程Q?/p>
#include <iostream.h>
#include <string.h>
void main(void)
{
char str1[] = { "Tsinghua "};
char str2[] = { "Computer"};
cout <<strcpy(str1,str2)<<endl;
}
q行l果QTsinghua Computer
注意Q在定义字符数组1的长度时应该考虑字符数组2的长度,因ؓq接后新字符串的长度Z个字W串长度之和。进行字W串q接后,字符?的结自动被LQ在l尾串末保留新字符串后面一个结?
4、strncat(char target[], const char source[], int numchars);
strncat:字W串source的前numchars个字W接到字W串target的后面?br />
strncat函数应用举例Q?br />
原型Qstrncat(char target[], const char source[], int numchars);
功能Q将字符串source的前numchars个字W接到字W串target的后?br />
例程Q?/p>
#include <iostream.h>
#include <string.h>
void main(void)
{
char str1[] = { "Tsinghua "};
char str2[] = { "Computer"};
cout <<strncat(str1,str2,3)<<endl;
}
q行l果QTsinghua Com
5、int strcmp(const char firststring[], const char secondstring);
strcmpQ?/strong>比较两个字符串firststring和secondstring?br />
strcmp函数应用举例
原型Qint strcmp(const char firststring[], const char secondstring);
功能Q比较两个字W串firststring和secondstring
例程Q?
#include <iostream.h>
#include <string.h>
void main(void)
{
char buf1[] = "aaa";
char buf2[] = "bbb";
char buf3[] = "ccc";
int ptr;
ptr = strcmp(buf2,buf1);
if(ptr > 0)
cout <<"Buffer 2 is greater than buffer 1"<<endl;
else
cout <<"Buffer 2 is less than buffer 1"<<endl;
ptr = strcmp(buf2,buf3);
if(ptr > 0)
cout <<"Buffer 2 is greater than buffer 3"<<endl;
else
cout <<"Buffer 2 is less than buffer 3"<<endl;
}
q行l果?Buffer 2 is less than buffer 1
Buffer 2 is greater than buffer 3
6、strlen( const char string[] );
strlenQ?/strong>l计字符串string中字W的个数。
strlen函数应用举例
原型Qstrlen( const char string[] );
功能Q统计字W串string中字W的个数
例程Q?
#include <iostream.h>
#include <string.h>
void main(void)
{
char str[100];
cout <<"误入一个字W串:";
cin >>str;
cout <<"The length of the string is :"<<strlen(str)<<"?<<endl;
}
q行l果The length of the string is x (xZ输入的字WL?
注意Qstrlen函数的功能是计算字符串的实际长度Q不包括'\0'在内。另外,strlen函数也可以直接测试字W串帔R的长度,如:strlen("Welcome")?
原文出处Q?a target="_blank" >codeprojectQCString
Management
通过阅读本文你可以学习如何有效地使用 CString?br />
CString
是一U很有用的数据类型。它们很大程度上化了MFC中的许多操作Q得MFC在做字符串操作的时候方便了很多。不怎样Q用CString有很多特D?
的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这文章就来讨些技巧?br />
使用CString可以让你对字W串的操作更加直截了当。这文章不是CString的完全手册,但囊括了大部分常见基本问题?br />
q篇文章包括以下内容Q?/p>
下面我分别讨论?br /> 1?b>CString cat("Cat");
CString graycat = gray + cat;
要比用下面的Ҏ好得多:
char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);
2?b>格式化字W串
与其?sprintf() 函数?wsprintf() 函数来格式化一个字W串Q还不如?CString 对象的Format()ҎQ?/p>
CString s;
s.Format(_T("The total is %d"), total);
用这U方法的好处是你不用担心用来存放格式化后数据的缓冲区是否_大,q些工作由CStringcL你完成?br /> 格式化是一U把其它不是字符串类型的数据转化为CStringcd的最常用技巧,比如Q把一个整数{化成CStringcdQ可用如下方法:
CString s;
s.Format(_T("%d"), total);
我LҎ的字W串使用_T()宏,q是Z让我的代码至有Unicode的意识,当然Q关于Unicode的话题不在这文章的讨论范围。_T()宏在8位字W环境下是如下定义的Q?/p>
#define _T(x) x // 非Unicode版本Qnon-Unicode versionQ?/pre>而在Unicode环境下是如下定义的:
#define _T(x) L##x // Unicode版本QUnicode versionQ?/pre>所以在Unicode环境下,它的效果q当于Q?/p>
s.Format(L"%d", total);如果你认Z的程序可能在Unicode的环境下q行Q那么开始在意用 Unicode ~码。比如说Q不要用 sizeof() 操作W来获得字符串的长度Q因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节,比如在我需要获得字W长? 的时候,我会用一个叫做DIM的宏Q这个宏是在我的dim.h文g中定义的Q我会在我写的所有程序中都包含这个文Ӟ
#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )q个宏不仅可以用来解决Unicode的字W串长度的问题,也可以用在编译时定义的表gQ它可以获得表格的项敎ͼ如下Q?br />class Whatever { ... };q里要提醒你的就是一定要注意那些在参C需要真实字节数的API函数调用Q如果你传递字W个数给它,它将不能正常工作。如下:
Whatever data[] = {
{ ... },
...
{ ... },
};
for(int i = 0; i < DIM(data); i++) // 扫描表格L匚wV?br />TCHAR data[20];
lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT造成以上原因是因为lstrcpyn需要一个字W个C为参敎ͼ但是WriteFile却需要字节数作ؓ参数?br /> 同样需要注意的是有时候需要写出数据的所有内宏V如果你仅仅只想写出数据的真实长度,你可能会认ؓ你应该这样做Q?/p>
WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG但是在Unicode环境下,它不会正常工作。正的做法应该是这P
WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT因ؓWriteFile需要的是一个以字节为单位的长度。(可能有些Z想“在非Unicode的环境下q行q行代码Q就意味着L在做一个多余的? 1操作Q这样不会降低程序的效率吗?”这U想法是多余的,你必要了解~译器实际上做了什么,没有哪一个C或C++~译器会把这U无聊的?操作留在代码 中。在Unicode环境下运行的时候,你也不必担心那个?操作会降低程序的效率Q记住,q只是一个左UM位的操作而已Q编译器也很乐意Z做这U替 换。)
使用_T宏ƈ不是意味着你已l创Z一个Unicode的程序,你只是创Z一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编 译你的程序的话,得到的将是一个普通的8-bit的应用程序(q里?-bit指的只是8位的字符~码Qƈ不是?位的计算机系l)Q当你在 Unicode环境下编译你的程序时Q你才会得到一个Unicode的程序。记住,CString ?Unicode 环境下,里面包含的可都是16位的字符哦?br />3?b>CString decimal = _T("4011");
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
4?b>
或者这P
CString graycat("Gray" + "Cat");事实上,~译器将抱怨上面的q些试。ؓ什么呢Q因为针对CString ?LPCTSTR数据cd的各U各Ll合Q?+?q算W? 被定义成一个重载操作符。而不是两?LPCTSTR 数据cdQ它是底层数据类型。你不能对基本数据(?int、char 或?char*Q类型重?C++ 的运符。你可以象下面这样做Q?/p>
CString graycat = CString("Gray") + CString("Cat");或者这P
CString graycat = CString("Gray") + "Cat";研究一番就会发玎ͼ?+”L使用在至有一?CString 对象和一? LPCSTR 的场合?br />
注意Q编写有 Unicode 意识的代码L一件好事,比如Q?/p>CString graycat = CString(_T("Gray")) + _T("Cat");q将使得你的代码可以直接UL?br />
char* 转化?CString
现在你有一?char* cd的数据,或者说一个字W串。怎么样创?CString 对象呢?q里有一些例子:char * p = "This is a test";或者象下面q样更具?Unicode 意识Q?/p>
TCHAR * p = _T("This is a test")?/p>
LPTSTR p = _T("This is a test");你可以用下面Q意一U写法:
CString s = "This is a test"; // 8-bit only
CString s = _T("This is a test"); // Unicode-aware
CString s("This is a test"); // 8-bit only
CString s(_T("This is a test")); // Unicode-aware
CString s = p;
CString s(p);用这些方法可以轻村ְ帔R字符串或指针转换?CString。需要注意的是,字符的赋值L被拷贝到 CString 对象中去的,所以你可以象下面这h作:
TCHAR * p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;l果字符串肯定是“GrayCat”?br />
CString c还有几个其它的构造函敎ͼ但是q里我们不考虑它,如果你有兴趣可以自己查看相关文档?br />
事实上,CString cȝ构造函数比我展C的要复杂,比如Q?/p>CString s = "This is a test";q是很草率的~码Q但是实际上它在 Unicode 环境下能~译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作?8 位字W串转换?16 位字W串。不怎样Q如?char * 指针是网l上传输?8 位数据,q种转换是很有用的?br />
CString 转化?char* 之一Q?/b>强制cd转换?LPCTSTRQ?br />
q是一U略微硬性的转换Q有关“正”的做法Qh们在认识上还存在许多混ؕQ正的使用Ҏ有很多,但错误的使用Ҏ可能与正的使用Ҏ一样多?br /> 我们首先要了?CString 是一U很Ҏ?C++ 对象Q它里面包含了三个|一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字W记C及一个缓冲区长度? 有效字符数的大小可以是从0到该~冲最大长度值减1之间的Q何数Q因为字W串l尾有一个NULL字符Q。字W记数和~冲区长度被巧妙隐藏?br /> 除非你做一些特D的操作Q否则你不可能知道给CString对象分配的缓冲区的长度。这P即你获得了?~冲的地址Q你也无法更改其中的内容Q不能截短字W串Q也 l对没有办法加长它的内容Q否则第一旉׃看到溢出?br /> LPCTSTR 操作W(或者更明确地说是 TCHAR * 操作W)?CString cM被重载了Q该操作W的定义是返回缓冲区的地址Q因此,如果你需要一个指?CString ? 字符串指针的话,可以q样做:CString s("GrayCat");
LPCTSTR p = s;它可以正地q行。这是由C语言的强制类型{化规则实现的。当需要强制类型{化时QC++规测容许q种选择。比如,你可以将QQҎQ定义ؓ某个复? Q有一ҎQҎQ进行强制类型{换后只返回该复数的第一个QҎQ也是其实部)。可以象下面q样Q?/p>
Complex c(1.2f, 4.8f);
float realpart = c;如果(float)操作W定义正的话,那么实部的的值应该是1.2?br /> q种强制转化适合所有这U情况,例如QQ何带?LPCTSTR cd参数的函数都会强制执行这U{换? 于是Q你可能有这样一个函敎ͼ也许在某个你买来的DLL中)Q?/p>
BOOL DoSomethingCool(LPCTSTR s);你象下面q样调用它:
CString file("c:\\myfiles\\coolstuff")
BOOL result = DoSomethingCool(file);它能正确q行。因?DoSomethingCool 函数已经说明了需要一?LPCTSTR cd的参敎ͼ因此 LPCTSTR 被应用于该参敎ͼ?MFC 中就是返回的串地址?br />
如果你要格式化字W串怎么办呢Q?/p>CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s", graycat);注意׃在可变参数列表中的|在函数说明中是以?..”表C的Qƈ没有隐含一个强制类型{换操作符。你会得C么结果呢Q?br /> 一个o人惊讶的l果Q我们得到的实际l果串是Q?/p>
"Mew! I love GrayCat"?/pre>因ؓ MFC 的设计者们在设?CString 数据cd旉常小心, CString cd表达式求值后指向了字W串Q所以这里看不到M? Format ?sprintf 中的强制cd转换Q你仍然可以得到正确的行为。描q?CString 的附加数据实际上?CString 名义地址之后?br /> 有一件事情你是不能做的,那就是修改字W串。比如,你可能会试用?”代曎?”(不要做这LQ如果你在乎国际化问题,你应该用十q制转换? National Language Support Ҏ,Q,下面是个单的例子Q?/p>
CString v("1.00"); // 货币金额Q两位小?br />LPCTSTR p = v;
p[lstrlen(p) - 3] = '','';q时~译器会报错Q因Z赋g一个常量串。如果你做如下尝试,~译器也会错Q?/p>
strcat(p, "each");因ؓ strcat 的第一个参数应该是 LPTSTR cd的数据,而你却给了一?LPCTSTR?br />
不要试图钻这个错误消息的牛角,q只会你自己陷入麻烦!
原因是缓冲有一个计敎ͼ它是不可存取的(它位?CString 地址之下的一个隐藏区域)Q如果你改变q个Ԍ~冲中的字符计数不会反映所做的修改。此外,如果字符串长度恰好是该字W串物理限制的长度(梢后q会讲到q? 个问题)Q那么扩展该字符串将改写~冲以外的Q何数据,那是你无权进行写操作的内存(不对吗?Q,你会毁换坏不属于你的内存。这是应用程序真正的M? 斏V?/p>CString转化成char* 之二Q?/b>使用 CString 对象?GetBuffer ҎQ?br />
如果你需要修?CString 中的内容Q它有一个特D的Ҏ可以使用Q那是 GetBufferQ它的作用是q回一个可写的~冲指针? 如果你只是打修改字W或者截短字W串Q你完全可以q样做:CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...
if(p != NULL)
*p = _T(''\0'');
s.ReleaseBuffer();q是 GetBuffer 的第一U用法,也是最单的一U,不用l它传递参敎ͼ它用默认? 0Q意思是Q“给我这个字W串的指针,我保证不加长它”。当你调?ReleaseBuffer Ӟ字符串的实际长度会被重新计算Q然后存? CString 对象中?br /> 必须一点,?GetBuffer ?ReleaseBuffer 之间q个范围Q一定不能用你要操作的q个~冲?CString 对象的Q何方法。因?ReleaseBuffer 被调用之前,?CString 对象的完整性得不到保障。研I以下代码:
CString s(...);
LPTSTR p = s.GetBuffer();
//... q个指针 p 发生了很多事?br />
int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的{案!!!
s.TrimRight(); // 很糟!!!!! 不能保证能正常工?!!!
s.ReleaseBuffer(); // 现在应该 OK
int m = s.GetLength(); // q个l果可以保证是正的?br />
s.TrimRight(); // 正常工作?/pre>假设你想增加字符串的长度Q你首先要知道这个字W串可能会有多长Q好比是声明字符串数l的时候用Q?/p>
char buffer[1024];表示 1024 个字W空间以让你做M惛_得事情。在 CString 中与之意义相{的表示法:
LPTSTR p = s.GetBuffer(1024);调用q个函数后,你不仅获得了字符串缓冲区的指针,而且同时q获得了长度臛_?1024 个字W的I间Q注意,我说的是“字W”,而不是“字节”,因ؓ CString 是以隐含方式感知 Unicode 的)?br /> 同时Q还应该注意的是Q如果你有一个常量串指针Q这个串本n的D存储在只d存中Q如果试囑֭储它Q即使你已经调用?GetBuffer Qƈ获得一个只d存的指针Q存入操作会p|Qƈ报告存取错误。我没有?CString 上证明这一点,但我看到q大把的 C E序员经常犯q个错误?br /> C E序员有一个通病是分配一个固定长度的~冲Q对它进?sprintf 操作Q然后将它赋值给一?CStringQ?/p>
char buffer[256];
sprintf(buffer, "%......", args, ...); // ... 部分省略许多l节
CString s = buffer;虽然更好的Ş式可以这么做Q?/p>
CString s;
s.Format(_T("%...."), args, ...);如果你的字符串长度万一过 256 个字W的时候,不会破坏堆栈?br />
另外一个常见的错误是:既然固定大小的内存不工作Q那么就采用动态分配字节,q种做法弊端更大Q?/p>int len = lstrlen(parm1) + 13 lstrlen(parm2) + 10 + 100;
char * buffer = new char[len];
sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);
CString s = buffer;
......
delete [] buffer;它可以能被简单地写成Q?/p>
CString s;
s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);需要注?sprintf 例子都不?Unicode qA的,管你可以?tsprintf 以及?_T() 来包围格式化字符Ԍ但是基本 思\仍然是在走弯路,q这样很Ҏ出错?br />
CString to char * 之三Q?/b>和控件的接口Q?br />
我们l常需要把一?CString 的g递给一个控Ӟ比如QCTreeCtrl。MFC为我们提供了很多便利来重载这个操作,但是 在大多数情况下,你用“原始”Ş式的更新Q因此需要将墨某个串指针存储?TVINSERTITEMSTRUCT l构?TVITEM 成员中。如下:TVINSERTITEMSTRUCT tvi;
CString s;
// ... 为s赋一些倹{?br />tvi.item.pszText = s; // Compiler yells at you here
// ... 填写tvi的其他域
HTREEITEM ti = c_MyTree.InsertItem(&tvi);Z么编译器会报错呢Q明明看h很完的用法啊!但是事实上如果你看看 TVITEM l构的定义你׃明白Q在 TVITEM l构?pszText 成员的声明如下:
LPTSTR pszText;
int cchTextMax;因此Q赋g是赋l一?LPCTSTR cd的变量,而且~译器无法知道如何将赋D句右边强制{换成 LPCTSTR。好吧,你说Q那我就Ҏq样Q?/p>
tvi.item.pszText = (LPCTSTR)s; //~译器依然会报错?/pre>~译器之所以依然报错是因ؓ你试图把一?LPCTSTR cd的变量赋值给一?LPTSTR cd的变量,q种操作在C或C++中是被禁止的。你不能用这U方? 来滥用常量指针与非常量指针概念,否则Q会Cؕ~译器的优化机制Q之不知如何优化你的程序。比如,如果你这么做Q?/p>
const int i = ...;
//... do lots of stuff
... = a[i]; // usage 1
// ... lots more stuff
... = a[i]; // usage 2那么Q编译器会以为既?i ?const Q所?usage1和usage2的值是相同的,q且它甚臌事先计算?usage1 处的 a[i] 的地址Q然后保留着在后面的 usage2 处用,而不是重新计。如果你按如下方式写的话Q?/p>
const int i = ...;
int * p = &i;
//... do lots of stuff
... = a[i]; // usage 1
// ... lots more stuff
(*p)++; // mess over compiler''s assumption
// ... and other stuff
... = a[i]; // usage 2~译器将认ؓ i 是常量,从?a[i] 的位|也是常量,q样间接地破坏了先前的假设。因此,你的E序会?debug ~译模式Q没有优化)?release ~译模式Q完全优化)中反映出不同的行为,q种情况可不好,所以当你试图把指向 i 的指针赋值给一? 可修改的引用Ӟ会被~译器诊断ؓq是一U伪造。这是Z么(LPCTSTRQ强制类型{化不起作用的原因?br /> Z么不把该成员声明?LPCTSTR cd呢?因ؓq个l构被用于读写控件。当你向控g写数据时Q文本指针实际上被当?LPCTSTRQ而当你从控gL? Ӟ你必L一个可写的字符丌Ӏ这个结构无法区分它是用来读q是用来写?br />
因此Q你会常常在我的代码中看到如下的用法Q?/p>tvi.item.pszText = (LPTSTR)(LPCTSTR)s;它把 CString 强制cd转化?LPCTSTRQ也是说先获得改字W串的地址Q然后再强制cd转化?LPTSTRQ以便可以对之进行赋值操作? 注意q只有在使用 Set ?Insert 之类的方法才有效Q如果你试图获取数据Q则不能q么做?br /> 如果你打获取存储在控g中的数据Q则ҎE有不同Q例如,Ҏ?CTreeCtrl 使用 GetItem ҎQ我惌取项目的文本。我知道q些 文本的长度不会超q?MY_LIMITQ因此我可以q样写:
TVITEM tvi;
// ... assorted initialization of other fields of tvi
tvi.pszText = s.GetBuffer(MY_LIMIT);
tvi.cchTextMax = MY_LIMIT;
c_MyTree.GetItem(&tvi);
s.ReleaseBuffer();可以看出来,其实上面的代码对所有类型的 Set Ҏ都适用Q但是ƈ不需要这么做Q因为所有的c?Set ҎQ包? InsertҎQ不会改变字W串的内宏V但是当你需要写 CString 对象Ӟ必须保证~冲是可写的Q这正是 GetBuffer 所做的事情。再ơ强调: 一旦做了一?GetBuffer 调用Q那么在调用 ReleaseBuffer 之前不要对这?CString 对象做Q何操作?br />
5?b> 6?b>
对于单个?BSTR 串来_q种用法可以工作得很好,q是因ؓ CString 有一个特D的构造函CLPCWSTRQBSTR正是q种cdQ? 为参敎ͼq将它{化成 ANSI cd。专门检查是必须的,因ؓ BSTR 可能为空|?CString 的构造函数对?NULL 值情况考虑的不是很周到Q(感谢 Brian Ross 指出q一?Q。这U用法也只能处理包含 NUL l结字符的单字符Ԍ如果要{化含有多?NULL 字符 Ԍ你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如h意,应该量避免?br /> Ҏ C/C++ 规则Q如果你有一?LPWSTRQ那么它别无选择Q只能和 LPCWSTR 参数匚w?br />
?Unicode 模式下,它的构造函数是Q?/p>CString::CString(LPCTSTR);正如上面所表示的,?ANSI 模式下,它有一个特D的构造函敎ͼ
CString::CString(LPCWSTR);它会调用一个内部的函数?Unicode 字符串{换成 ANSI 字符丌Ӏ(在Unicode模式下,有一个专门的构造函敎ͼ该函数有一个参数是LPCSTRcd——一??ANSI 字符? 指针Q该函数它加宽?Unicode 的字W串Q)再次Q一定要?BSTR 的值是否ؓ NULL?br /> 另外q有一个问题,正如上文提到的:BSTRs可以含有多个内嵌的NULL字符Q但?CString 的构造函数只能处理某个串中单?NULL 字符? 也就是说Q如果串中含有嵌入的 NUL字节QCString 会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函敎ͼ你会发现 它们都调用了lstrlenQ也是计算字符串的长度?br /> 注意?Unicode ?ANSI 的{换用带专门参数?::WideCharToMultiByteQ如果你不想使用q种默认的{换方式,则必ȝ写自q转化代码?br /> 如果你在 UNICODE 模式下编译代码,你可以简单地写成Q?br />
CString convert(BSTR b)如果?ANSI 模式Q则需要更复杂的过E来转换。注意这个代码用与 ::WideCharToMultiByte 相同的参数倹{所以你 只能在想要改变这些参数进行{换时使用该技术。例如,指定不同的默认字W,不同的标志集{?
{
if(b == NULL)
return CString(_T(""));
CString s(b); // in UNICODE mode
return s;
}CString convert(BSTR b)我ƈ不担心如?BSTR 包含没有映射?8 位字W集?Unicode 字符时会发生什么,因ؓ我指定了::WideCharToMultiByte 的最后两个参Cؓ NULL。这是你可能需要改变的地方?
{
CString s;
if(b == NULL)
return s; // empty for NULL BSTR
#ifdef UNICODE
s = b;
#else
LPSTR p = s.GetBuffer(SysStringLen(b) + 1);
::WideCharToMultiByte(CP_ACP, // ANSI Code Page
0, // no flags
b, // source widechar string
-1, // assume NUL-terminated
p, // target buffer
SysStringLen(b)+1, // target buffer length
NULL, // use system default char
NULL); // don''t care if default used
s.ReleaseBuffer();
#endif
return s;
}7?b> return s;
case VT_I4 | VT_BYREF:
s.Format(_T("%d"), *va->plVal);
case VT_R8:
s.Format(_T("%f"), va->dblVal);
return s;
... 剩下的类型{换由读者自己完?br /> default:
ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
return CString("");
} /* vt */
}8?b>载入字符串表资源
如果你想创徏一个容易进行语a版本UL的应用程序,你就不能在你的源代码中直接包含本土语a字符? Q下面这些例子我用的语言都是pQ因为我的本土语是英语)Q比如下面这U写法就很糟Q?pre>CString s = "There is an error";你应该把你所有特定语a的字W串单独摆放Q调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:
s.Format(_T("%d - %s"), code, text);在你的程序中Q文字字W串不是语言敏感的。不怎样Q你必须很小心,不要使用下面q样的串Q?/p>
// fmt is "Error in %s file %s"
// readorwrite is "reading" or "writing"
s.Format(fmt, readorwrite, filename);q是我的切n体会。在我的W一个国际化的应用程序中我犯了这个错误,管我懂徯Q知道在徯的语法中动词攑֜句子的最后面Q我们的德国斚w的发行h q是苦苦的抱怨他们不得不提取那些不可思议的d语错误提CZ息然后重新格式化以让它们能正常工作。比较好的办法(也是我现在用的办法Q是使用两个字符 Ԍ一个用 于读Q一个用于写Q在使用时加载合适的版本Q得它们对字符串参数是非敏感的。也是说加载整个格式,而不是加载串 “reading”,“writing”:
// fmt is "Error in reading file %s"
// "Error in writing file %s"
s.Format(fmt, filename);一定要注意Q如果你有好几个地方需要替换,你一定要保证替换后句子的l构不会出现问题Q比如在p中,可以是主?宾语Q主?谓语Q动?宾语的结构等{?br /> 在这里,我们q不讨论 FormatMessageQ其实它?sprintf/Format q要有优势,但是不太Ҏ和CString l合使用。解册U问题的办法是我们按照参数出现在参数表中的位置l参数取名字Q这样在你输出的时候就不会把他们的位置排错了?br /> 接下来我们讨论我们这些独立的字符串放在什么地斏V我们可以把字符串的值放入资源文件中的一个称?STRINGTABLE 的段中。过E如下:首先使用 Visual Studio 的资源编辑器创徏一个字W串Q然后给每一个字W串取一个IDQ一般我们给它取名字都以 IDS_开头。所以如果你有一个信息,你可以创Z个字W串资源然后取名?IDS_READING_FILEQ另外一个就取名? IDS_WRITING_FILE。它们以下面的Ş式出现在你的 .rc 文g中:
STRINGTABLE
IDS_READING_FILE "Reading file %s"
IDS_WRITING_FILE "Writing file %s"
END注意Q?/b>q些资源都以 Unicode 的格式保存,不管你是在什么环境下~译。他们在Win9xpȝ上也是以Unicode 的Ş式存在,虽然 Win9x 不能真正处理 Unicode?br /> 然后你可以这样用这些资源:
// 在用资源串表之前,E序是这样写的:CString fmt;// 使用资源串表之后Q程序这样写Q?
if(...)
fmt = "Reading file %s";
else
fmt = "Writing file %s";
...
// much later
CString s;
s.Format(fmt, filename);CString fmt;现在Q你的代码可以移植到M语言中去。LoadString Ҏ需要一个字W串资源?ID 作ؓ参数Q然后它?STRINGTABLE 中取出它对应的字W串Q赋值给 CString 对象? CString 对象的构造函数还有一个更加聪明的特征可以?STRINGTABLE 的用。这个用法在 CString::CString 的文档中没有指出Q但是在 构造函数的CZE序中用了。(Z么这个特性没有成为正式文档的一部分Q而是攑֜了一个例子中Q我C得了Q)——?b>译者注Q从q句话看Q作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检? 】。这个特征就是:如果你将一个字W串资源的ID强制cd转换?LPCTSTRQ将会隐含调?LoadString。因此,下面两个构造字W串的例子具有相同的效果Q而且?ASSERT 在debug模式下不会被触发Q?pre>CString s;
if(...)
fmt.LoadString(IDS_READING_FILE);
else
fmt.LoadString(DS_WRITING_FILE);
...
// much later
CString s;
s.Format(fmt, filename);
s.LoadString(IDS_WHATEVER);
CString t( (LPCTSTR)IDS_WHATEVER );
ASSERT(s == t);//不会被触发,说明s和t是相同的?/pre>现在Q你可能会想Q这怎么可能工作呢?我们怎么能把 STRINGTABLE ID 转化成一个指针呢Q很单:所有的字符?ID 都在1~65535q个范围内,也就是说Q它所有的高位都是0Q而我们在E序中所使用的指针是不可能小?5535的,因ؓE序的低 64K 内存永远也不可能存在的,如果你试图访?x00000000?x0000FFFF之间的内存,会引发一个内存越界错误。所以说1~65535的g 可能是一个内存地址Q所以我们可以用q些值来作ؓ字符串资源的ID?br /> 我們于?MAKEINTRESOURCE 宏显式地做这U{换。我认ؓq样可以让代码更加易于阅诅R这是个只适合?MFC 中用的标准宏。你要记住,大多数的Ҏ卛_以接受一?UINT 型的参数Q也可以接受一?LPCTSTR 型的参数Q这是依?C++ 的重载功能做到的。C++重蝲函数带来?弊端是造成所有的强制cd转化都需要显C声明。同P你也可以l很多种l构只传递一个资源名?/p>
CString s;
s.LoadString(IDS_WHATEVER);
CString t( MAKEINTRESOURCE(IDS_WHATEVER));
ASSERT(s == t);告诉你吧Q我不仅只是在这里鼓吹,事实上我也是q么做的。在我的代码中,你几乎不可能扑ֈ一个字W串Q当Ӟ那些只是偶然在调试中出现的或者和语言无关的字W串除外?br />
9?b>
char* szName = GetName().GetBuffer(20);
RegSetValueEx(hKey, "Name", 0, REG_SZ,
(CONST BYTE *) szName,
strlen (szName + 1));q个 Name 字符串的长度于 20Q所以我不认为是 GetBuffer 的参数的问题?br />
真让人困惑,请帮帮我?br />
亲爱?FrustratedQ?br />
你犯了一个相当微妙的错误Q聪明反被聪明误Q正的代码应该象下面这PCString Name = GetName();Z么我写的代码能行而你写的有问题呢?主要是因为当你调?GetName 时返回的 CString 对象是一个时对象。参见:《C++ Reference manual》?2.2
RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,
(CONST BYTE *) (LPCTSTR)Name,
(Name.GetLength() + 1) * sizeof(TCHAR));
在一些环境中Q编译器有必要创Z个时对象,q样引入临时对象是依赖于实现的。如果编译器引入的这个时对象所属的cL构造函数的话,~译器要保q个cȝ构造函数被调用。同LQ如果这个类声明有析构函数的话,也要保证q个临时对象的析构函数被调用?br /> ~译器必M证这个时对象被销毁了。被销毁的切地点依赖于实?....q个析构函数必须在退出创临时对象的范围之前被调用?br /> 大部分的~译器是q样设计的:在时对象被创徏的代码的下一个执行步骤处隐含调用q个临时对象的析构函敎ͼ实现hQ一般都是在下一个分号处。因此, q个 CString 对象?GetBuffer 调用之后p析构了(Z提一句,你没有理q GetBuffer 函数传递一个参敎ͼ而且没有使用ReleaseBuffer 也是不对的)。所?GetBuffer 本来q回的是指向q个临时对象中字W串的地址的指针,但是当这个时对象被析构后,q块内存p释放了。然?MFC 的调试内存分配器会重Cؓq块内存全部填上 0xDDQ显C出来刚好就是?amp;Yacute;”符受在q个时候你向注册表中写数据Q字W串的内容当然全被破坏了?br /> 我们不应该立xq个临时对象转化?char* cdQ应该先把它保存C?CString 对象中,q意味着把时对象复制了一份,所以当临时?CString 对象被析构了之后Q这?CString 对象中的g然保存着。这个时候再向注册表中写数据没有问题了?br /> 此外Q我的代码是h Unicode 意识的。那个操作注册表的函数需要一个字节大,使用lstrlen(Name+1) 得到的实际结果对? Unicode 字符来说?ANSI 字符要小一半,而且它也不能从这个字W串的第二个字符起开始计,也许你的本意?lstrlen(Name) + 1QOKQ我承认Q我也犯了同L错误Q)。不论如何,?Unicode 模式下,所有的字符都是2个字节大,我们需要处理这个问题。微软的文档令h惊讶地对此保持缄默:REG_SZ 的值究竟是以字节计还是以字符计算呢?我们假设它指的是以字节ؓ单位计算Q你需要对你的代码做一些修Ҏ计算q个字符串所含有的字节大?br />10?b>s += SomeCString4;
比v下面的代码来Q效率要低多了:
char s[1024];
lstrcpy(s, SomeString1);
lstrcat(s, SomeString2);
lstrcat(s, SomeString 3);
lstrcat(s, ",");
lstrcat(s, SomeString4);MQ你可能会想Q首先,它ؓ SomeCString1 分配一块内存,然后?SomeCString1 复制到里面,然后发现它要做一个连接,则重新分配一块新的够大的内存,大到能够放下当前的字W串加上SomeCString2Q把内容复制到这块内? Q然后把 SomeCString2 q接到后面,然后释放W一块内存,q把指针重新指向新内存。然后ؓ每个字符串重复这个过E。把q?4 个字W串q接h效率多低啊。事实上Q在很多情况下根本就不需要复制源字符Ԍ?+= 操作W左边的字符Ԍ?br /> ?VC++6.0 中,Release 模式下,所有的 CString 中的~存都是按预定义量子分配的。所谓量子,即确定ؓ 64?28?56 或?512 字节。这意味着除非字符串非帔RQ连接字W串的操作实际上是 strcat l过优化后的版本Q因为它知道本地的字W串应该在什么地方结束,所以不需要寻扑֭W串的结;只需要把内存中的数据拯到指定的地方卛_Q加上重新计字 W串的长度。所以它的执行效率和U?C 的代码是一LQ但是它更容易写、更Ҏl护和更Ҏ理解?br /> 如果你还是不能确定究竟发生了怎样的过E,L?CString 的源代码Qstrcore.cppQ在?vc98的安装目录的 mfc\src 子目录中。看?ConcatInPlace ҎQ它被在所有的 += 操作W中调用?br />
啊哈Q难?CString 真的q么"高效"吗?比如Q如果我创徏CString cat("Mew!");然后我ƈ不是得到了一个高效的、精?个字节大的~冲区(4个字W加一个结束字W)Q系l将l我分配64个字节,而其?9个字节都被浪费了?br /> 如果你也是这么想的话Q那么就请准备好接受再教育吧。可能在某个地方某个人给你讲q尽量用少的空间是件好事情。不错,q种说法的确正确Q但是他忽略了事实中一个很重要的方面?br /> 如果你编写的是运行在16K EPROMs下的嵌入式程序的话,你有理由量用空_在这U环境下Q它能你的E序更健壮。但是在 500MHz, 256MB的机器上?Windows E序Q如果你q是q么做,它只会比你认为的“低效”的代码q行得更p?br /> 举例来说。字W串的大被认ؓ是媄响效率的首要因素Q字符串尽可能可以提高效率,反之则降低效率,q是大家一贯的x。但是这U想法是不对的,_? 的内存分配的后果要在程序运行了好几个小时后才能体现得出来,那时Q程序的堆中充满小片的内存Q它们太以至于不能用来做Q何事Q但是他们增加了你程 序的内存用量Q增加了内存面交换的次敎ͼ当页面交换的ơ数增加到系l能够忍受的上限Q系l则会ؓ你的E序分配更多的页面,直到你的E序占用了所有的可用 内存。由此可见,虽然内存片是决定效率的ơ要因素Q但正是q些因素实际控制了系l的行ؓQ最l,它损害了pȝ的可靠性,q是令h无法接受的?br /> CQ在 debug 模式下,内存往往是精分配的Q这是ؓ了更好的排错?br /> 假设你的应用E序通常需要连l工作好几个月。比如,我常打开 VC++QWordQPowerPointQFrontpageQOutlook ExpressQForté AgentQInternet Explorer和其它的一些程序,而且通常不关闭它们。我曄夜以l日地连l用 PowerPoint 工作了好几天Q反之,如果你不q怸得不使用?Adobe FrameMaker q样的程序的话,你将会体会到可靠性的重要Q这个程序机会每天都要崩?~6ơ,每次都是因ؓ用完了所有的I间q填满我所有的交换面Q。所以精内存分配是不可取的Q它会危及到pȝ的可靠性,q引起应用程序崩溃?br /> 按量子的倍数为字W串分配内存Q内存分配器可以回收用q的内存块,通常q些回收的内存块马上可以被其它?CString 对象重新用到Q这样就可以保证片最。分配器的功能加ZQ应用程序用到的内存p可能保持最,q样的程序就可以q行几个星期或几个月而不出现? 题?br /> 题外话:很多q以前,我们?CMU 写一个交互式pȝ的时候,一些对内存分配器的研究昄出它往往产生很多内存片。Jim MitchellQ现在他?Sun Microsystems 工作Q那时侯他创造了一U内存分配器Q它保留了一个内存分配状늚q行时统计表Q这U技术和当时的主分配器所用的技术都不同Q且较ؓ领先。当一个内存块 需要被分割得比某一个值小的话Q他q不分割它,因此可以避免产生太多到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个Q动指针,他认为: 与其让指令做长时间的存取内存操作Q还不如单的忽略那些太小的内存块而只做一些Q动指针的操作。(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.Q他是对的?br /> 永远不要认ؓ所谓的“最优化”是建立在每一行代码都高速且节省内存的基上的Q事实上Q高速且节省内存应该是在一个应用程序的整体水^上考虑的。在软g的整体水q上Q只使用最内存的字符串分配策略可能是最p糕的一U方法?br /> 如果你认Z化是你在每一行代码上做的那些努力的话Q你应该想一惻I在每一行代码中做的优化很少能真正v作用。你可以看我的另一关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》?br /> CQ?= q算W只是一U特例,如果你写成下面这P
CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;则每一?+ 的应用会造成一个新的字W串被创建和一ơ复制操作?br />ȝ
以上是?CString 的一些技巧。我每天写程序的时候都会用到这些。CString q不是一U很难用的c,但是 MFC 没有很明昄指出q些特征Q需要你自己L索、去发现?img src ="http://www.tkk7.com/tingfeng/aggbug/261265.html" width = "1" height = "1" />
]]>