字符编码

  对于字符串,我想,第一想法是以0结尾的ANSI单字节字符数组。然而有些语言文字系统的字符集容量巨大,一个字节最多能表示256个符号,显得不够用。于是产生了DBCS,但其单双字节混用,犹如恶梦,我们鄙弃之,转而研究Unicode编码。

  在windows Vista中,每个Unicode字符都使用UTF-16编码,UTF的全称是Unicode Transformation Format。UTF-16将每个字符编码为2个字节。这是通用的。当然,有些语言,2个字节也不够,对此,对这些语言,UTF-16支持使用代理(surrogate),后者用4个字节表示一个字符。另外,还有一些其他标准,如UTF-8、UTF-32等。

  我们知道,c语言用char表示一个ANSI字符。默认下,编译器将字符串中的字符转化为8位char构成的一个数组,如:

char c='a';

char s[100]="hello world!';

  Microsoft的C/C++编译器定义了一个内建的数据类型wchar_t,它表示一个16位的Unicode字符。因为早期版本的Microsoft编译器没有提供这个内建的数据类型,所以编译器只有在指定了/Zc:wchar_t编译器开关时,才会定义这个数据类型。默认情况下,在Microsoft visual studio中新建一个c++项目时,这个编译器开关是指定的。建议始终指定这个编译器开关,这样才能更好地操纵Unicode字符。

  声明Unicode字符和字符串的方法如下:

wchar_t c=L'a';

wchar_t s[100]=L"hello world!";

字符串之前的大写字母L通知编译器该字符串应该编译为一个Unicode字符串。

为了与c语言稍微有些区分,windows开发团队希望自己定义数据类型,于是在WinNT.h中定义了以下数据类型:

typedef char CHAR;
typedef wchar_t WCHAR;

除此之外,还有很多,比如:

typedef CHAR *PCHAR;
typedef CHAR *PSTR;
typedef CONST CHAR *PCSTR;

typedef WCHAR *PWCHAR;
typedef WCHAR *PWSTR;
typedef COSNT WCHAR *PCWSTR;

自windows NT起,windows的所有版本都完全用Unicode来构建。但是如果函数传入一个ANSI字符串,那么就要进行ANSI到Unicode的转换,再把结果传给操作系统。这个转换工作,会产生时间和内存上的开销。

如果一个windows函数的参数列表中有字符串,则该函数通常有两个版本。例如,一个CreateWindowEx接受Unicode字符串,另一个CreateWindowEx则接受ANSI字符串。虽然事实如此,但两个函数的原型为:

CreateWindowExW这个版本接受Unicode字符串,函数名末尾的大写字母W代码wide。Unicode字符常常被称作宽字符。CreateWindowExA接受ANSI字符串。

但在平时,我们只是在自己的代码中调用CreateWindowEx,不会直接调用CreateWindowExW或CreateWindowExA。在WinUser.h中,CreateWindowEx实际上是一个宏,它的定义如下:

#ifdef UNICODE
#define CreatewindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif

用visual studio创建一个新项目时,它默认会定义UNICODE。所以,默认调用CreateWindowExW。

在windows Vista中,CreateWindowExA的源代码只是一个转换层(translation layer),它负责分配内存,以便将ANSI字符串转换为Unicode字符串。然后,代码会调用CreateWindowExW,并向它传送转换后的字符串。CreateWindowExW返回时,CreateWindowExA会释放它的内存缓冲区,并将窗口句柄返回。所以,对于要在缓冲区中填充字符串的任何函数,在应用程序能够处理字符串之前,系统必须先将Unicode字符串转换为非Unicode形式。由于系统必须执行这些转换,故而应用程序需要更多内存,且运行速度缓慢。为避免这种情况,一开始就应该使用Unicode来开发程序。另外,目前已知的windows的这些转换函数中存在一些bug,所以避免使用它们还有助于消除一些潜在的bug。

windows API中的一些函数存在的唯一意义就是为了向后兼容16位windows程序,因为后者只支持ANSI字符串。在开发的新程序中,应避免使用它们。如,在使用WinExec和OpenFile调用的地方,应该用CreateProcess和CreateFile函数调用来代替。

Microsoft将COM从16位windows移植到win32时,做出一个重要决策:所有需要字符串作为参数的COM接口方法都只接受Unicode字符串。这是明智的。

和windows函数一样,c运行库也提供了一系列函数处理ANSI字符,并提供了另一系列函数处理Unicode字符。然而,与windows不同的是,两个系列的函数是各自为政、自力更生的,即ANSI系列函数不会转换字符串为Unicode形式,同样Unicode系列函数不会内部调用ANSI版本。

在c运行库中,strlen就是个返回ANSI字符串长度的函数,与之对应的是wcslen---返回Unicode字符串长度。他们的原型都在string.h中。为了使源码既能用ANSI编译,又能用Unicode编译,还必须包含TChar.h,该文件定义了以下宏:

#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif

现在,应该在代码中调用_tcslen。默认情况下,在visual studio中新建一个c++项目时,已经定义了_UNICODE。

针对不属于c++标准一部分的标识符,c运行库始终会为他们附加下划线前缀。但是,windows团队并没有这样做。所以,在应用程序中,应确保要么同时定义了UNICODE和_UNICODE,要么一个都不要定义。CmnHdr.h头文件中定义了UNICODE和_UNICODE,可以避免这个问题。

原文地址:https://www.cnblogs.com/jiu0821/p/4658462.html