###ASCⅡ码#
在ASCⅡ码制定的过程中,对于代码是6、7、8位争议不断,由于可靠性的原因,舍弃了6位,而由于成本问题(上世纪60年代来说,8位代价是非常昂贵的),选择了7位编码。于是最后代码共有26个小写字母,26个大写字母,10个数字,32个符号,33个控制码,一个空格码,共128个代码组成了ASCⅡ码,成为了全球标准。
在小型计算机发展时期,8位一字节的标准得到巩固,于是决定以一个字节来储存字符,也就是8位编码,这样就有了128个额外的字符来补充ASCⅡ。
###多字节字符集DBCS#
256字符的字符集难以满足如中文、日文、韩文以及世界各个国家的各种文字需求,于是产生了多字节字符集(DBCS)。DBCS前128个代码就是ASCⅡ码,而较高的128个中有些还跟随有第二个字节,这两个字节在一起表示一个单独的字符,如一个汉字。
DBCS的问题在于,有些字符是单个字节组成的,例如ASCⅡ码,有些字符是两个字节组成的,这导致了字符串的字符长度不能由字节数量决定,字符串的长度需要解析后才知道,每个字节都要被检查是不是双字节的前导字节。
###Unicode字符集#
很明显的是,DBCS是一种很笨的解决方法,也产生了很多问题。作为程序员,我们知道,与其混合使用1字节和2字节代码的DBCS,倒不如用一种全都是2字节16位编码的字符集,这就是Unicode字符集。
Unicode被认为是“宽字符”,每个Unicode的字符是16位而不是8位,在多字节字符集里我们仍处理8位字符,而在Unicode字符集里我们不再处理8位字符。最开始的128个Unicode字符(0x0000到0x007F)是ASCⅡ码,而之后的128个Unicode字符(0x0080到0x00ff)是ASCⅡ码的扩展码。希腊字母表从0x0370到0x03ff,汉语日语韩语从0x3000到0x9FFF代码。
Unicode的优点是只有一个字符集,避免了二义性,而其缺点是内存占比是ASCⅡ的两倍。
现在再来看char和wchar_t:
char a = 'A'; //定义了一个被0x41初始化的一个字节存储空间
char *p = "hello!"; //windows是32位系统,指针p占用4个字节空间存储p的地址
//字符串hello!存储在指针p指向的内存地址及其后6个字节,共7字节
//包括'h''e''l''l''o''!''\0'
int s = strlen(p); //s=6; 记住这里
wchar_t wa = 'A'; //定义了一个被0x0041初始化的两个字节存储空间
wchar_t *wp = L"hello!"; //L代表了是宽字节字符,这里,hello!包括最后的\0共占用了14个字节
int s = strlen(wp); //s=1; 因为strlen接受的是8位字符,它把h也就是0x0068
//分为了0x68和0x00(内存存储顺序是高位高地址,低位低地址)
//当读完0x68后,strlen判断读到0x00也就是字符串结束符,就结束输出了
wchar_t用了2个字节来存储字符,所以大部分处理char类型的函数都不能正常工作了,但是我们有代替的,比如strlen的宽字节版本wcslen。在strlen定义的位置,也就是STRING.H中,strlen的声明如下:
size_t __cdecl strlen(cosnt char*);
而wcslen声明在了WCHAR.H中:
size_t __cdecl wcslen(const wchar_t*);
所以求wp的长度应该写为:
int s = wcslen(wp); //求得s=6,即为wp中字符个数
几乎所有的字符函数都有宽字符版本,例如printf的宽字符版本为sprintf,这些都定义在了WCHAR.H中。
###关于TCHAR.H#
Unicode也有很多的缺点,比如空间的占用以及某些方面的不兼容等等,我们希望维护两个版本,一个用ASCⅡ码而另一个用Unicode字符集,但并不希望创建两套源代码文件,所以有了TCHAR.H头文件。TCHAR为需要字符或字符串作为参数的普通库函数(例如printf,strlen,sprintf,wcslen)提供了一系列的替代名称(例如_tprintf,_tcslen),这些名称是“通用”的,因为他们可以指Unicode和非Unicode版本函数。
由于TCHAR不是标准的一部分,所以定义的每个函数和宏都有一个下划线前缀。
定义方式如下:
#ifdef UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
以此类推其他函数定。
THCAR.H也提供了一个TCHAR类型来解决两个字符数据类型的问题。如果UNICODE标识符被定义了,
typedef wchar_t TCHAR;
否则的话,TCHAR就是char
typedef char TCHAR;
关于宽字符字符串前缀L的解决办法如下,如果UNICODE被定义了
#define __T(x) L##x
预处理将L和宏参数拼接在一起,例如x是hello,则L##x就是L"hello"
如果UNICODE没有被定义,则__T宏就定义如下:
#define __T(x) x
然后
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
所以写字符串的时候只需要将字符串字面写在宏内就可以了
_TEXT("hello!");
这样如果UNICODE被定义了,字符串就解释为宽字符组成的,如果没有被定义,就解释为8位字符组成的。
###关于char16_t和char32_t
随着Unicode的发展,类型wchar_t已经不足以满足需求,比如在字符串编码时,如果有特定的符号和长度特征的类型将很有帮助,而wchar_t的符号和长度不同的C/C++库有不同的规定。因此C++11新增加了char16_t和char32_t。
char16_t:无符号类型,长16位,char32_t无符号类型,长32位
前缀u和U分别指出字符字面值的类型为char16_t和char32_t。
来源:oschina
链接:https://my.oschina.net/u/2760487/blog/704727