Windows核心编程【2】小结

来源:互联网 发布:python应聘有什么要求 编辑:程序博客网 时间:2024/05/18 11:28

第二章 字符和字符串处理


一、字符编码

1、常规C、C++语言学习中习惯单字节(8个bits,256个字符)ANSI字符编码。
2、双字节字符集(double-byte character,DBCS),一个字符串中的每个字符都由1个或2个字节组成。以日本汉字为例,如果第一个字符在0x81到0x9f之间,或者在0xe0到0xfc之间,就必须检查下一个字节,才能判断出一个完整的汉字。这样某些字符是1个字节宽,而又的字符确实2个字节宽,程序员与之打交道不和谐。
3、Unicode是1990年由Apple和Xerox共同建立的一项标准,1991年,由一些公司成立专门的协会来开发和推动Unicode。
4、在Windows Vista中,每个Unicode字符都适用UTF-16编码,UTE全称是Unicode Transformation Format(Unicode转换格式)。UTF-16将每个字符编码为2个字节。不过16位不足以表示某些语言的所有字符。对于这些语言,UTF-16支持适用代理(surrogates),后缀是用32位来表示一个字符的一种方式。
5、UTF-32将每个字符编码为4个字节,不需要关心代理问题。不过某些情况下不是一种高效的编码格式。(相对国外而言吧)
6、目前,Unicode为阿拉伯语、汉语拼音等字符(书写符号(scripts))定义了码位(code point,即一个符号在字符集中的位置)。每个版本的Unicode都在现有的书写符号的基础上引入了新的字符。

二、ANSI和Unicode字符和字符串数据类型

1、C语言的char数据类型表示8位的ANSI字符。默认情况下,C编译器会把字符串中的字符转换成由8位char数据类型构成的一个数组。
2、Microsoft的C/C++编译器定义了一个内建的数据结构wchar_t,表示UTF-16字符。因为早期MS编译器没有提供这个内建的数据类型,所以编译器只有在指定了/Zc:wchar_t编译开关时才定义这个数据类型。
3、声明Unicode字符和字符串的方法:
wchar_t c = L'a';
wchar_t szBuffer[100] = L"A string";
大写字母L通知编译器该字符编译为Unicode字符串。
4、WinNT.h定义如下:
typedef char CHAR;
typedef wchar_t WCHAR;
5、在写代码的时候,可以让它使用ANSI或Unicode字符/字符串都能通过编译。
TCHAR szBuffer[100] = TEXT("A string");
如果定义了UNICODE,就是由16位字符构成的一个数组;否则就是8位字符的一个数组。

三、Windows中的Unicode和ANSI函数

1、自Windows NT起,Windows的所有版本都完全用Unicode来构建。也就是说,所有核心函数都需要Unicode字符串。调用一个Windows函数时,如果向它传入一个ANSI字符串,函数首先会把字符串转换为Unicode,再把结果传给操作系统。
2、Windows函数需要获取一个字符串作为参数的时候,通常该函数会有两个版本。A是ANSI,W是Unicode的(宽字符)。Ex版本通常会根据是否定义了UNICODE宏定义为对于的函数版本。
3、用VS创建一个新的项目的时候,会默认定义了UNICODE。
4、Vista中ANSI基本上是调用Unicode版本,只是进行了中间转换。在开发DLL的时候,也可参考这种技术,提供到处两个函数,一个Unicode版本,一个ANSI版本。在ANSI版本中,只是分配内存,执行必要的字符串转换,然后调用该函数的Unicode版本。
5、COM接口都只接受Unicode字符串。

四、C运行库中的Unicode和ANSI函数

1、ANSI版本是“自力更生”,不借助于Unicode版本。
2、在C运行库中,能返回ANSI字符串长度的函数例子是strlen,与之对应的是wcslen,既是Unicode字符串的长度。两个原型都在String.h中定义,为了使源代码针对、ANSI或Unicode都能编译,需要借助于TChar.h,有宏定义如下:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
3、针对不属于C++标准一部分的标识符,C运行库始终为其附加了下划线前缀。
4、在Windows程序中,应确保要么同时定义了UNICODE和_UNICODE宏,要不一个都不定义。

五、C运行库中的安全字符串函数

1、任何修改字符串的函数都存在一个安全隐患:如果目标字符串缓冲区不够大,无法包含所生成的字符串,就会破坏内存中的数据(或者说发生“内存恶化”,即memory corruption)。
2、MS在StrSafe.h中定义了新的安全字符串函数,String.h在里面也被包含了。
3、C运行库中现有的字符串处理函数,比如_tcscpy宏背后的那些函数,已标记为废弃不用。
4、现有的每一个函数,比如_tcscpy或)tcscat,都有一个对应的新版本的函数。签名的名称相同,但最后添加了一个_s(代表secure)后缀。
5、安全函数特征,一个可写的缓冲区作为参数传递时,必须同时提供它的大小。这个值应该是一个字符数。为你的缓冲区使用_countof宏(在stdlib.h中定义),很容易计算出这个值。函数会返回一个errno_t值来指出成功或失败。然而,这些函数并不实际地返回。相反,如果是一次debug build,会显示一个弹窗,然后再终止程序。而在release build中,则直接终止。同时相关写入缓冲第一个字符为‘\0’,其填充为0xfd。
6、C运行时实际上允许我们提供自己的函数,在它检测到一个无效参数时,调用此函数。————InvalidParameterHandler
7、在所有已定义的变量后面,内存会用0xcc这个值来填充。这是编译器执行进行时检查(/RTCs,/RTCc或/TRC1)的结果,它们会在运行时自动检测缓冲区溢出。如果不用这些标志来编译代码,在内存视图中,则会一个接一个地显示所有变量。在build版本中,应该指出执行运行时间差。
8、除了新的安全字符串函数,C运行库还新增了一些函数,用于在执行字符串处理时提供更多控制。自然,C运行库同时为这些函数提供了ANSI(A)版本和Unicode(W)版本。在方法名称中,含有一个“Cch”,表示“Count of characters”,即字符数,通常用_countof宏来获取此值;还有一系列名称中含有“Cb”的函数,这些函数要求用字节数来指定大小,而不是用字符数,通常使用sizeof操作符来获取此值。
9、上面提到的另一些函数,不同于安全(后缀_s)的函数,当缓冲区过小的时候,这些函数会执行截断。为了判断是否发生这种情况,可以检测返回值。

10、Windows也提供了各种字符串处理函数,其中许多函数已经不赞成使用了,因为也无法检测缓冲区溢出问题。于此同时,ShlwApi.h定义了大量方便好用的字符串函数,可以用来对操作系统有关的数值进行格式化操作,比如StrFormatKBSize。

六、为何要用Unicode

1、Unicode使程序的本地化变得更容易。
2、使用Unicode,只需发布一个二进制(.exe或DLL)文件,即可支持所有语言。
3、Unicode提升了应用程序的效率,因为代码执行速度更快,占用内存更少。Windows内部的一切工作都是使用Unicode字符和字符串来进行的。所以,假如你非要传入ANSI字符或字符串,Windows就会被迫分配内存,并将ANSI字符或字符串转换为等价的Unicode形式。
4、使用Unicode,你的应用程序能轻松调用所有不反对使用(nondeprecated)的Windows函数,因为一些Windows函数提供了只能处理Unicode字符和字符串的版本。
5、使用Unicode,你的代码很容易与COM集成(后者要求使用Unicode字符和字符串)。
6、使用Unicode,你的代码很容易与.NET Framework集成(后者要要求使用Unicode字符和字符串)。
7、使用Unicode,能保证你的代码能够轻松操纵你自己的资源(其中的字符串总是Unicode的)

七、推荐的字符和字符串处理方式

1、开始将文本字符串想象为字符的数组,而不是char或字节的数组。
2、为文本字符和字符串使用泛型(比如TCHAR/PTSTR)。
3、修改字符串算术问题,既是字符数和字节数的区分。
4、避免使用printf系的函数,尤其是不要用%s和%S字段类型来进行ANSI与字符串的相互转换。正确的做法是使用MultiByteToWideChar和WideCharToMultiByte函数。
5、字符串操作,不想明确控制截断,则用StringCch系列安全字符串处理函数,否则选用后缀为_s的函数。

八、Unicode和ANSI字符串转换

1、Windows函数MultiByteToWideChar将多字节字符串转换为宽字符串。还有WideCharToMultiByte。
2、判断文本是Unicode还是ANSI的函数,IsTextUnicode函数,不过没有任何硬性判断,既是不是一定正确。

原创粉丝点击