《Windows核心编程》第二章专门介绍编程中的字符和字符串编码以及相关库函数,主要涉及”Unicode“和”ANSI“。书上描述的非常透彻,在此,我将它的主要思想精简并概括如下:
一、字符(串)类型
ANSI和Unicode是两种不同的字符编码方式。ANSI一般以一个字节(8bits)来编码所有的字符,理论上可以表示256种字符;Unicode,一般指UTF-16(Unicode Transformation Format),它用两个字节(16bits)来编码所有的字符,理论上可以表示65536种字符。
VC++中,char表示ANSI字符类型,占8位;wchat_t表示UTF-16,占16位,它是VC++编译器中的内置类型,对应编译开关为”/Zc:wchar_t“,默认开启。声明Unicode字符和字符串如下:
// a 16-bit character
wchar_t c = L'A';
// an array up to 99 16-bit characters and a 16-bit terminating zero
wchar_t szBuffer[100] = L"A String";
L是编译器选项,表示Lager,用于通知编译器该字符应当编译为一个Unicode。 sz - string zero,表示以0结尾的字符串。
此外,WinNT.h中也定义了大写的STRING、CHAR和WCHAR等typedef类型。值得注意的是,它还定义了宏TEXT,该宏可以兼容ANSI和Unicode。
// if UNICODE defined, a 16-bit character; else a 8-bit character
TCHAR c = TEXT('A');
// if UNICODE defined, an array of a 16-bit character; else a 8-bit character
TCHAR szBuffer[100] = TEXT("A String");
STRING是对C库中的string的封装,防止缓冲区溢出,如下:
typedef _STRING
{
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING;
二、Windows API
大多数Windows API都提供了两个版本,一个接受Unicode,一个接受ANSI。如CreateWindowExW和CreateWindowExA,其中W - wide,A - ANSI。实际调用的时候,我们只调用CreateWindowEx,因为它内部是这样实现的:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif
在 VS Solution 的“属性 - 常规”中切换“字符集”:多字节/Unicode,再观察 “C/C++ - 命令行”中的编译选项:
多字节版本:
/D "_MBCS"
Unicode 版本:
/D "_UNICODE" /D "UNICODE"
事实上,我们自己在写DLL导出函数的时候,也可以按照上述方法,提供Unicode和ANSI两个版本的导出函数。而且,在实现的时候,只需要实现Unicode版本的函数,而让ANSI版本的函数实现时,先转换入参为Unicode,再调Unicode版本,返回之前再转换回ANSI即可。
三、C运行时库的字符(串)函数
C运行时库也提供了两个版本,如:strlen和wcslen。其中,wc - wchar的头两个字母,s - str,它是后来才添加的,命名规则比较简单,就是在ANSI版本函数前添加前缀wc,并缩写str为s。
此外,TChar.h中也提供了一套兼容方法:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
tc - text char, s - str
注意,它的宏是_UNICODE,多了一个下划线。
四、VC的安全字符串函数
任何修改字符串的函数都存在一个安全隐患:如果目标字符串缓存区不够大,无法容纳所生成的字符串,就会导致内存中的数据被破坏(memory corruption),即缓冲区溢出。为此,微软定义了一套安全的字符串函数,替代C运行库的字符串函数,它们包含在”StrSafe.h“头文件中。
新函数的命名规则比较简单,就是在原函数的基础上,添加”_s“后缀。s - secure。对比如下:
PTSTR _tcscpy (PTSTR strDestination, PCTSTR strSource);
errno_t _tcscpy_s(PTSTR strDestination, size_t numberofCharacters,
PCTSTR strSource);
第二个参数,指定缓冲区大小,它表示的是字符数,可以通过”_countof宏“计算(在stdlib.h中)。注意,sizeof计算的是字节数,_countof计算的字符数,对于Unicode,它一个字符占用两个字节,最好使用_countof。
新函数会检测一些项目,如:指针不为NULL,整数在有效范围内,缓冲区足够大。如果失败,新函数并不会返回,而是直接终止程序,防止被黑。
此外,还有一批添加”Cch前缀“的字符串函数,它可以提供用户更多控制。Cch - Count of characters。
五、ANSI和Unicode转换函数
MultiByteToWideChar和WideCharToMultiByte
六、”IsTextUnicode“判断打开的文本是否是Unicode