上面一节提到了字符的表示方式,这一节主要讲他们的由来以及微软给出的解决方案。
在《Windows核心编程中》有提到,从Windows NT开始,Windows的所有版本都完全用Unicode来构建。这个意思就是说,即使你用的是ASCII字符或者ASCII字符串(一个字符一个字节),都将在内部转换成Unicode字符。在大部分的Windows的API中都有xxxxxxA、xxxxxxW以及xxxxxxEx等三个前缀相同的版本,后缀为A表示使用的ANSI版本(不是ASCII),后缀为W表示使用Unicode版本(w的意思是wide),以及中间兼容版本Ex。若使用_UNICODE宏和UNICODE宏,Ex就就用Unicode版本,反之则使用ANSI版本,这是微软想到解决兼容的一种办法。
自从VC6.0开始,就已经支持UNICODE字符集,但是VC6.0默认的是“多字节字符集”(MBCS),使用UNICODE字符集需要自行在项目中定义UNICODE宏和_UNICODE宏;然而VS.NET2002以及更高版本默认的却是UNICODE字符集,即定义了UNICODE宏和_UNICODE宏,除非你在项目属性中人工指定使用“多字节字符集”(MBCS),那样就不会定义UNICODE宏和_UNICODE宏。
VS2013的MFC更是完全废除了MBCS,使用MBCS会给编译错误信息:MBCS is depreciated。
之前有提到过ANSI字符集,也有很多套,但是计算机怎么知道要用哪套字符集呢?于是提出了一套“治标不治本”的方案,这就是“ANSI代码页”。每种字符集规定一个数字作为唯一标识符,叫做“代码页”,更换这个唯一标识符就切换字符集。例如ASCII的代码页是437,latin-1的代码页是1252,GB2312-80的代码页是20936,GBK的代码页是936,BIG-5的代码页是950,GB18030的代码页是54936等等。
起初Windows就是用“ANSI代码页”切换方案,字符串用char数组来存储,叫做“多字节字符集(MBCS)”,从上一篇知道,这么叫的原因是因为有些字符不止占一位,一个GBK汉字可能占两位。
切换代码页是治标不治本,可能在这台计算机上能够编译成功,但是到了别人电脑,使用了不一样的代码页,就只能看到一堆乱码。于是为了统一文字,Unicode字符集出现了。【ISO国际标准】根据ISO的C标准和C++标准,字符类型有两种:一种叫做“窄字符”(narrow character),用char表示,每个单位占一个字节;另一种叫做“宽字符”(wide character),用wchar_t表示,每个单位占的字节数必须多于char,但没有规定具体占几个字节。窄字符和宽字符的表示方式分别是:char s[] = “narrow character” 和 wchar_t ws[] = L"wide character" 。
微软在C标准之外,私自定义了“C风格窄字符串”和“C风格宽字符串”:
#define CONST const
typedef char CHAR;
typedef CHAR *NPSTR, *LPSTR, *PSTR;
typedef CONST CHAR *LPCSTR, *PCSTR;
#define CONST const
typedef wchar_t WCHAR;
typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef CONST WCHAR *LPCWSTR, *PCWSTR;
为了在ANSI字符集和Unicode字符集之前切换,还定义一种TCHAR字符类型,在中定义如下:
typedef wchar_t WCHAR;
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR;
typedef LPWSTR PTSTR, LPTSTR;
typedef LPCWSTR PCTSTR, LPCTSTR;
#else
typedef char TCHAR, *PTCHAR;
typedef LPSTR PTSTR, LPTSTR;
typedef LPCSTR PCTSTR, LPCTSTR, PCUTSTR, LPCUTSTR;
#endif
还定义了TEXT宏:
#ifdef UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
在头文件中,还定义了_T宏和_TEXT宏:
#ifdef _UNICODE
#define __T(x) L ## x
typedef wchar_t TCHAR;
#else
#define __T(x) x
typedef char TCHAR;
#endif
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
ISO的C和C++标准也有对应的窄字符集函数printf、strcat、strcmp和宽字符集函数wprintf、strcat、strcmp等等。微软也私自在中宏定义了这些函数的TCHAR风格类型:
#ifdef _UNICODE
#define _tprintf wprintf
#define _tcscmp wcscmp
#define _tcscat wcscat
#else
#define _tprintf printf
#define _tcscmp strcmp
#define _tcscat strcat
#endif
跨平台应该注意:
- 【ISO国际标准】ISO的C标准和C++标准只规定了窄字符char为一个字节,宽字符wchar_t的字节数大于char。没有规定wchar_t具体占几个字节。没有TCHAR这个东西,不存在头文件。
- 【微软方案】在微软的编译器中,char每个单位占一个字节,表示ANSI字符集;wchar_t占两个字节,表示UTF-16小端模式。可以用TCHAR类型(在头文件中),并用是否定义UNICODE宏和_UNICODE宏来切换。
- 【Linux通常情况】在大多数Linux下的编译器中,char每个单位占一个字节,通常表示UTF-8字符集;wchar_t占四个字节,表示UTF-32字符集,大小端与CPU相关。没有TCHAR这种东西。
以上就是今天的总结,希望大家能够指出错误,定会加以改进。