目录
1.基本概念
计算机存储的信息都是用二进制数表示的;而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。通俗的说,按照何种规则将字符存储在计算机中,如‘a’用什么表示,称为“编码”;反之,将存储在计算机中的二进制数解析显示出来,称为“解码”,如同密码学中的加密和解密。在解码过程中,如果使用了错误的解码规则,则导致‘a’解析成‘b’或者乱码。
1.1字符集
字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集,Unicode字符集等。计算机要准确处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
1.2字符编码
字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间建立对应关系,它是信息处理的一项基本技术。通常人们用符号集合(一般情况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不同状态的组合来存储和处理信息的。元件不同状态的组合能代表数字系统的数字,因此字符编码就是将符号转换为计算机可以接受的数字系统的数,称为数字代码。
1.3字符集与字符编码的发展历程
1.3.1ASCII
(1).ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以勉强显示其他西欧语言。它是现今最通用的单字节编码系统(但是有被Unicode追上的迹象),并等同于国际标准ISO/IEC 646。
(2).ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
(3).ASCII编码:将ASCII字符集转换为计算机可以接受的数字系统的数的规则。使用7位(bits)表示一个字符,共128字符;但是7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符对ASCII进行了扩展,ASCII扩展字符集使用8位(bits)表示一个字符,共256字符。ASCII字符集映射到数字编码规则如下图所示:
(4).ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语,而随着科技发展,欧洲国家也开始普及计算机,ASCll最多表示128个字符已经明显不够用了,随后EASCII(Extended ASCII,延伸美国标准信息交换码)由此应运而生,EASCII码比ASCII码扩充出来的符号包含表格符号、计算符号、希腊字母和特殊的拉丁符号,而EASCII虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的苹果电脑已经抛弃ASCII而转用Unicode。
1.3.2GB2312
EASCII码对于部分欧洲国家基本够用了。但过后的不久,计算机便来到了中国,要知道汉字是世界上包括符号最多而且也是最难学的文字。 据不全然统计。汉字共包括了古文、现代文字等近10万个文字,就是我们如今日经常使用的汉字也有几千个。那么对于仅仅包括256个字符的EASCII码也难以满足需求了。因此GB 2312 或 GB 2312-80(GB为国标汉语拼音的首字母)应运而生,共包括7445个字符,6763个汉字和682个其它字符(拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母),基于EUC存储方式,每一个汉字及符号以两个字节来表示。第一个字节为“高位字节”,第二个字节为“低位字节”。
1.3.3BIG5
由于中国大陆指定的GB2312编码并不包括繁体字,因此,台湾单独制定了一套BIG5编码,并开始编写并推出BIG5标准。 之后推出的倚天中文系统则基于BIG5码。并在台湾地区取得了巨大的成功。在BIG5诞生后,大部分的电脑软件都使用了Big5码。BIG5对于以台湾为核心的亚洲繁体汉字圈产生了久远的影响。以至于后来的window 繁体中文版系统在台湾地区也基于BIG5码进行开发,其共收录13,060个汉字及441个符号,用两个字节来为每一个字符编码,第一个字节称为“高位字节”,第二个字节称为“低位字节”。
1.3.4ISO/IEC 8859、ISO/IEC 10646 / UCS
在计算机进入中国大陆的同样时期,计算机也迅速发展进入了世界各个国家。特别是对于亚洲国家而言。每一个国家都有自己的文字。于是每一个国家或地区都像中国大陆这样去制定了自己的编码标准,以便能在计算机上正确显示自己国家的符号。但带来的结果就是国家之间谁也不懂别人的编码,谁也不支持别人的编码。于是,世界相关组织意识到了这个问题,并開始尝试制定统一的编码标准。以便可以收纳世界全部国家的文字符号。
(1).ISO/IEC小组在1984年成立后的第三年(即1987年)开始启动ISO 8859标准的编写,ISO 8859是一系列8位字符集的标准。主要为世界各地的不同语言(除CJK)而单独编写的字符集,当中ISO/IEC 8859-1至ISO/IEC 8859-4四个项目早在1982年就已经编写出来,仅仅只是是由ANSI与ECMA合作完毕,并于1985年正式发布。ISO/IEC小组成立后。这一成果被其收录。并改名为ISO/IEC 8859 前四个项目。
(2).1993年,ISO/IEC 10646标准第一次发表,ISO/IEC 10646是ISO 646的扩展,定义了1个31位的字符集。ISO 10646标准中定义的字符集为UCS,UCS是Universal Character Set的缩写。中文译作通用字符集。最初的ISO 10646-1:1993的编码标准。即Unicode 1.1。收录中国大陆、台湾、日本及韩国通用字符集的汉字共计20,902个,当然每一个版本号的Unicode标准的字符集所包括的字符数不尽同样。UCS包括了已知语言的全部字符,除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语,还包括中文、日文、韩文这种方块文字,此外还包括了大量的图形、印刷、数学、科学符号。 UCS给每一个字符分配一个唯一的代码,而且赋予了一个正式的名字,通常在表示一个Unicode值的十六进制数的前面加上“U+”,比如“U+0041”代表字符“A”。
(3).UCS是一个超大的字符集。关于UCS制定的编码方案有两种:UCS-2和UCS-4,Unicode默认以UCS-2编码。顾名思义。UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。那么UCS-2事实上能够容纳的字符数为65536(2的16次方),而UCS-4能够容纳的字符数为2147483648(2的31次方),事实上对于UCS-2已经是全然够用了,基本能够包括世界全部国家的经常使用文字,假设须要考虑一些偏僻字,那么UCS-4则绝对能够满足了。
1.3.5UTF
Unicode 诞生。随之而来的计算机网络也发展了起来。Unicode 怎样在网络上传输也是一个必须考虑的问题,于是在1992年,面向网络传输的UTF标准出现了。 UTF是UnicodeTransformation Format的缩写。中文译作Unicode转换格式。事实上我们从如今能够把Unicode看作是一个标准或组织,而UCS就是一个字符集。那么UCS在网络中的传输标准就是UTF了。 前面提到了UCS的编码实现方式为UCS-2和UCS-4,即要么是每一个字符为2个字节。要么是4个字节。假设一个仅包括基本7位ASCII字符的Unicode文件,每一个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0,这就造成了比较大的浪费。可是,聪明的人们发明了UTF-8,UTF-8采用可变字节编码。这样能够大大节省带宽。并添加网络传输效率。
1.3.5.1UTF-8
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。
使用1~4个字节为每一个UCS中的字符编码:
(1).128个ASCII字符仅仅需一个字节编码(Unicode范围由U+0000至U+007F)
(2).拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母须要二个字节编码(Unicode范围由U+0080至U+07FF)
(3).大部分国家的经常使用字(包含中文)使用三个字节编码,其它极少使用的生僻字符使用四字节编码。
优点:(1).在处理经常会用到的ASCII字符方面非常有效。在处理扩展的拉丁字符集方面也不比UTF-16差。对于中文字符来说,比UTF-32要好。由位操作的天性使然,使用UTF-8不再存在字节顺序的问题了。一份以utf-8编码的文档在不同的计算机之间是一样的比特流。总体来说,在Unicode字符串中不可能由码点数量决定显示它所需要的长度,或者显示字符串之后在文本缓冲区中光标应该放置的位置;组合字符、变宽字体、不可打印字符和从右至左的文字都是其归因。所以尽管在UTF-8字符串中字符数量与码点数量的关系比UTF-32更为复杂,在实际中很少会遇到有不同的情形。
(2).UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串,所以现存的ASCII文本不需要转换。为传统的扩展ASCII字符集设计的软件通常可以不经修改或很少修改就能与UTF-8一起使用。
(3).使用标准的面向字节的排序例程对UTF-8排序将产生与基于Unicode代码点排序相同的结果。
(4).UTF-8和UTF-16都是可扩展标记语言文档的标准编码。所有其它编码都必须通过显式或文本声明来指定。
(5).任何面向字节的字符串搜索算法都可以用于UTF-8的数据(只要输入仅由完整的UTF-8字符组成)。但是,对于包含字符记数的正则表达式或其它结构必须小心。
(6).UTF-8字符串可以由一个简单的算法可靠地识别出来。就是,一个字符串在任何其它编码中表现为合法的UTF-8的可能性很低,并随字符串长度增长而减小。举例说,字符值C0,C1,F5至FF从来没有出现。为了更好的可靠性,可以使用正则表达式来统计非法过长和替代值。
缺点:因为每个字符使用不同数量的字节编码,所以寻找串中第N个字符是一个O(N)复杂度的操作 — 即,串越长,则需要更多的时间来定位特定的字符。同时,还需要位变换来把字符编码成字节,把字节解码成字符。
1.3.5.2UTF-16/UCS-2
UCS-2的父集。使用2个或4个字节来为每一个UCS中的字符编码:
(1).128个ASCII字符需两个字节编码
(2).其它字符使用四个字节编码
1.3.5.3UTF-32/UCS-4
(1)对于全部字符都使用四个字节来编码。UTF-32又称为UCS-4,是一种将Unicode字符编码的协定,对每个字符都使用4字节。就空间而言,是非常没有效率的。这种方法也有其优点,最重要的一点就是可以在常数时间内定位字符串里的第N个字符,因为第N个字符从第4×Nth个字节开始。虽然每一个码位使用固定长定的字节看似方便,它并不如其它Unicode编码使用得广泛。
(2)对于UTF-32和UTF-16编码方式还有一些其他不明显的缺点。不同的计算机系统会以不同的顺序保存字节。这意味着字符U+4E2D在UTF-16编码方式下可能被保存为4E 2D或者2D 4E,这取决于该系统使用的是大尾端(big-endian)还是小尾端(little-endian)。(对于UTF-32编码方式,则有更多种可能的字节排列。)只要文档没有离开你的计算机,它还是安全的——同一台电脑上的不同程序使用相同的字节顺序(byte order)。但是当我们需要在系统之间传输这个文档的时候,也许在万维网中,我们就需要一种方法来指示当前我们的字节是怎样存储的。不然的话,接收文档的计算机就无法知道这两个字节4E 2D表达的到底是U+4E2D还是U+2D4E。
(3)为了解决这个问题,多字节的Unicode编码方式定义了一个"字节顺序标记(Byte Order Mark)",它是一个特殊的非打印字符,你可以把它包含在文档的开头来指示你所使用的字节顺序。对于UTF-16,字节顺序标记是U+FEFF。如果收到一个以字节FF FE开头的UTF-16编码的文档,你就能确定它的字节顺序是单向的(one way)的了;如果它以FE FF开头,则可以确定字节顺序反向了。
1.3.6GB13000
前面提到了Unicode的迅速发展,至1993年时。包括CJK的Unicode 1.1已经公布了,中国也意识到了须要一个更大的字符集来走向世界。于是在同一年,中国大陆制定了基本等同于Unicode1.1的GB13000.1-93国家编码标准(简称GB13000),此标准等同于 ISO/IEC 10646.1:1993和Unicode 1.1。
1.3.7GBK
之前的国家编码标准GB 2312,基本满足了汉字的计算机处理须要。它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字。GB 2312不能处理,因此微软利用了GB2312中未使用的编码空间,收录了GB13000中的全部字符制定了汉字内码扩展规范GBK(K为汉语拼音 Kuo Zhan中“扩”字的首字母)。所以这一关系事实上是大陆把Unicode1.1借鉴过来改名为了GB13000。而微软则利用GB2312中未使用的编码空间收录GB13000制定了GBK。所以GBK是向下全然兼容GB2312的,共收录21886个字符, 当中汉字21003个, 字符883个,GBK仅仅只是是把GB2312中未使用的空间。编码了其它字符,所以GBK相同是用两个字节为每一个字符进行编码。
1.3.8GB18030
微软到了99年前后。说GBK已经落伍了,如今流行UTF-8标准,准备全盘转换成UTF-8,但中国编写并强制推出了GB18030标准。GB18030的诞生另一个原因是GBK仅仅包括了大部分的汉字和繁体字等。但中国有56个民族,当中有12个民族有自己的文字,于是在2000年。电子工业标准化研究所起草了GB18030标准,项目代号“GB 18030-2000”,全称《信息技术-信息交换用汉字编码字符集-基本集的扩充》。此标准推出后。在中国大陆之后的所售产品必须强制支持GB18030标准。GB18030收录了GBK中的全部字符。并将Unicode中其它中文字符(少数民族文字、偏僻字)也一并收录进来又一次编码。当中GB 18030-2000共收录27533个汉字,而GB 18030-2005共包括70244个汉字,采用多字节编码,每一个字符由1或2或4个字节进行编码。
1.3.9ANSI与OEM
(1).系统有当前使用的语言的设置,这个叫做LCID,不过这个设置其实还不能唯一决定系统的代码页,因为在这个设置之下还有个ANSI和OEM的设置。
(2).ANSI是系统的一般意义上的缺省代码页,而OEM则主要用于console程序(某些console相关的API比如WriteConsoleA缺省接受OEM编码的字符串,其他API则缺省接受ANSI编码的字符串),以前还有个MAC,不过由于现在Macintosh都采用UNICODE,所以这个设置实际上不用了。
(3).对于中文来讲,ANSI代码页跟OEM代码页都是GBK,但是英文的ANSI代码页是1252,而OEM代码页是437,所以说如果我们设置语言为英文,那么不论是你从某个API函数得到的字符串,还是说你自己创建一个字符串传入某些函数,都有两种可能:采用ANSI编码或者OEM编码。
(4).SetFileApisToOEM/ SetFileApisToANSI设置某些文件相关API是使用/接受ANSI还是OEM字符串;CharToOem/ OemToChar则在系统当前的OEM代码页与UNICODE或者系统当前的ANSI代码页之间进行转换;GetACP/ GetOEMCP可以得到当前系统的ANSI和OEM代码页设置,等等。
(5).如果我们不与Console打交道,一般我们所说的MBCS是指的当前系统的ANSI代码页,我们从用户输入中得到的文字也是这个代码页的编码。所以在一般情况下,我们不用关心二者之间的差别,在使用某些函数比如WideCharToMultiByte时,只需要用CP_ACP指定当前系统的代码页或者自己明确指定代码页即可。
2.UNICODE字符集与多字节字符集
2.1VS配置下的区别
UNICODE:采用定长编码方式,存储任何字符时都用两个字节储存。
ANSI:多字节字符集,采用不定长编码方式,存储英文字符时一个字节对应一个字符,而存储中文汉字时一个汉字采用两个字节存储。
在VS集成开发环境下,”使用Unicode字符集”时编译器会增加宏定义(#define UNICOE 1),而“使用多字节字符集”时,不会增加该宏定义。而增加该宏定义会影响一些将字符串作为输入参数的Windows API的使用,例如MessageBox。
Windows API对MessageBox的定义如下:
WINUSERAPI //#define WINUSERAPI DECLSPEC_IMPORT #define DECLSPEC_IMPORT __declspec(dllimport)
int
WINAPI //#define WINAPI __stdcall
MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
由#ifdef UNICODE可知,如果增加了宏定义UNICODE,则MessageBox函数会被宏定义为MessageBoxW,函数名称中的W意为宽字符(UNICODE),否则,如果没有宏定义UNICODE,则MessageBox函数会被宏定义为MessageBoxA,函数名称中的A意为ANSI(多字节字符)。
将项目配置中的字符集设为“使用Unicode字符集”,可以得到如下图所示结果:
在该配置下MessageBox函数传入的实参类型会出现错误,原因在于编译器增加了宏定义UNICODE,因此使用MessageBox函数相当于使用MessageBoxW函数,而MessageBoxW函数第二个参数的参数类型为LPCWSTR(const wchar_t*,详情见后文),因为字符串常量为const char*(LPCSTR)类型,因此出现实参与形参不兼容,有两种方法消除该错误,第一种时在字符串常量前面加上“L”,将在字符串用Unicode编码转换成const wchar_t后再传入函数,即可消除报错,如上图,第二种方法是将编译器配置字符集设置为多字节字符集,如下图:
此时第二个MessageBox实参报错是因为编译器此时字符集已经设置为了多字节字符集,因此MessageBox函数被宏定义为了MessageBox,实参应传入const char类型,而“国庆节快乐”字符串此时已经被“L”转成了const wchar_t*类型。
此外,可以通过#ifdef UNICODE #else #endif判断是否定义了UNICODE宏,也可以调用API函数MultiByteToWideChar和WideCharToMultiByte更换参数类型。
2.2char和wchar_t的区别
char和wchar_t是c++中表示字符的两种基本数据类型:
(1).char叫多字节字符,一个char占一个字节,之所以叫多字节字符是因为它表示一个字时可能是一个字节也可能是多个字节。一个英文字符(如’a’)用一个char(一个字节)表示,一个中文汉字(如’国’)用个char(两个字节)表示。测试代码如下:
#include <stdio.h>
int main() {
char str0[3] = "ab";
char str1[3] = "国";
char str2[5] = "国庆";
char str3[7] = "国庆节";
getchar();
return 0;
}
首先我们查看局部变量窗口:
英文字符串"ab",在内存中占用了三个char(字节)的空间,字符’a’,‘b’各占一个,最后一个字节存储’\0’,代表字符串结束;中文字符串"国庆",在内存中占用了五个char(字节)的空间,字符“国”和“庆”各占两个,同样,最后一个字节存储’\0’,代表字符串结束。我们再来通过存储地址查看这两个字符串在内存中的存储状态为:
此处61对应字符’a’的ASCll码,62对应字符’b’的ASCll码,第三个字节存储字符’\0’,用来表示字符串结束,在内存中存储为00。
此处"国庆"字符串存储的编码方式为GBK,最后一字节存储’\0’表示字符串结束,不懂可以去网页端进行在线编码转换查询,如下图:
(2).wchar_t被称为宽字符,一个wchar_t占2个字节。之所以叫宽字符是因为所有的字都要用两个字节(即一个wchar_t)来表示,不管是英文还是中文。测试代码如下:
#include <stdio.h>
int main() {
wchar_t str0[3] = L"ab";
wchar_t str1[2] = L"国";
wchar_t str2[3] = L"国庆";
wchar_t str3[4] = L"国庆节";
getchar();
return 0;
}
此时字符串的大小发生了变化,加上“L”是为了把const char类型的字符串常量转换为const wchar_t类型,局部变量窗口如下:
我们看到此时不管是英文还是中文,都用一个wchar_t的空间(两个字节)存储一个字符,再来看看内存情况:
此时可以看到不管中英文wchar_t都用两个字节存储,即使是’\0’也是这样,但此时中文“国庆”的编码方式发生了变化,wchar_t存储的编码方式为UNICODE(UCS-2),可以参考下图:
在这里字符在内存中的存储方式发生了一点小小的变化,从上面看出,无论是在UTF-8还是在多字节字符集下,中文的存储和加载都是顺序的,而在UNICODE编码下,字符"国"在内存和磁盘中存储顺序为:fd 56,实际上它的编码顺序为:56 fd,可以理解为一种为了编程和运行而存在的存储形态,因为栈,无论是在底层运行,还是软件设计架构上,都是客观的存在。所以次序颠倒的unicode能更加快速的编解码、运行,当unicode存储、加载时,计算机先加载第一个字节,再加载第二个字节,这样在内存和磁盘中交互,当编解码输出时,计算机先加载后写入的字节,再加载先写入的字节,在解释器里,正好编码的次序就编程了顺序,这样不需要再颠倒什么次序,立刻就能解析这是哪个字。这也是为什么在内存中,指针/内存地址,完全是以一个字节为单位的完全倒序。虽然不知道现在的运行机制是否受这一两个字节的影响,但基础机制大概仍是以一个字节为单位,如果强行以两个字节为单位的话,UTF-8就乱了。但unicode的颠倒次序存储机制确实有栈的影子。
2.2LPCSTR和LPCWSTR的区别
LPCSTR宏定义如下:
typedef char CHAR;
typedef _Null_terminated_ CONST CHAR *LPCSTR, *PCSTR;
LPCWSTR宏定义如下:
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef _Null_terminated_ CONST WCHAR *LPCWSTR, *PCWSTR;
由此可知,LPCSTR即为const char*,而LPCWSTR即为const w_chart*。
此外VC++中还有一些其他常见的宏,如LPSTR,LPCSTR,LPTSTR,LPCTSTR,LPWSTR,LPCWSTR,含义如下:
LPSTR:32bit指针 指向一个字符串,每个字符占1字节。相当于char *
LPCSTR:32-bit指针 指向一个常字符串,每个字符占1字节。相当于const char *
LPTSTR:32-bit指针 每字符可能占1字节或2字节,取决于Unicode是否定义,相当于tcahr*
LPCTSTR:32-bit指针 指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义,相当于const tchar*
LPWSTR: 32-bit指针,指向一个unicode字符串的指针,每个字符占2字节,相当于wchar_t*
LPCWSTR:32-bit指针, 指向一个unicode字符串常量的指针,每个字符占2字节,相当于const wchar_t*
LP:long pointer,长指针,表示这是一个指针类型。
C:constant,常量,表示用该类型定义的变量只可赋初值不可修改
T: T表示_T宏,这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。
W:wide,表示Unicode编码,两个字节一个字符
STR:表示这个变量是一个字符串。
对应关系如下表:
类型 | MBCS(Multi-Byte Character Set) | UNICODE |
---|---|---|
LPSTR | char* | char* |
LPCSTR | const char* | const char* |
LPTSTR | TCHAR*(或char*) | TCHAR* (或wchar_t*) |
LPCTSTR | const TCHAR* | const TCHAR* |
LPWSTR | wchar_t* | wchar_t* |
LPCWSTR | const wchar_t* | const wchar_t* |
WCHAR | wchar_t | wchar_t |
TCHAR | char | wchar_t |