宽字符wchar_t 与 窄字符char
先说下窄字符char,这个大部分读者应该很清楚,char类型的变量占一个字节(byte)(也就是8个bit(比特)),能表示256个字符,那char的范围有两种
第一种(signed char):-128~127
第二种(unsigned char):0~255
(对char的范围感兴趣的读者可以看一下这篇文章:浅谈char类型范围)
但C标准并没有规定char 应该是unsigned还是signed,C标准定义了三种类型:char、signed char、unsigned char在不同的编译器下char可能是有符号数,也有可能是无符号数(意思是取决于编译器)
那么怎样确定char是有符号数还是无符号数呢,有两种方法
方法一:使用CHAR_MIN(注意:CHAR_MIN这个宏是在stdlib.h这个头文件中定义的)
运行结果:
方法二:给char类型的变量赋值负数(如果char在编译器上是有符号数,那么赋值只要是大于等于-128的数都可以正常打印)
运行结果:
上述两种方法都可以用来确定char是无符号数还是有符号数(感兴趣的读者可以自行测试一下char的边界,如果char是有符号数,可以给char赋值127或128来看一下会出现什么结果)
现在来说下宽字符wchar_t,先来看下char和wchar_t在存储空间上的差别
运行结果:
从上面可以看出char占一个字节,wchar_t占两个字节
下面来确定wchar_t是有符号数还是无符号数
运行结果:
从上面的结果可以看出wchar_t为无符号数,因为wchar_t占两个字节,也就是16个比特(bit),最大值就是216-1=65535,到这里读者可以看出宽字符和窄字符最大的区别就是占字节大小的不同
宽字符 和 窄字符的赋值
关于窄字符char,大部分读者都知道赋值的方法
或者
而宽字符的赋值就不太一样
与窄字符相比,前面多了一个大写的L,这个L的作用就是告诉编译器,这个字符串按照宽字符来存储(一个字符占两个字节)
按照之前的说法宽字符中一个字符占2个字节,那么mm[20]应该占40个字节,长度应该为11,下面来验证一下
运行结果:
上面的代码中用到了一个函数wcslen(),这个函数和strlen()其实是一个作用,只不过strlen适用于窄字符,wcslen适用于宽字符(读者可以理解为wcslen是strlen对应的一个宽字符版本函数)
在C语言中的每个字符串处理函数都有对应的宽字符处理版本,下面列举一些常见的宽字符处理函数
(图片出处:https://www.cnblogs.com/mr-wid/archive/2012/10/07/2714392.html)
还有一点需要读者注意的是,宽字符不等于Unicode,Unicode 是宽字符编码的一种,只不过最常见的宽字符编码方式就是Unicode了,UTF-16和UTF-32都是Unicode编码。wchar_t也主要以这两种方式实现
( c/c++标准只是声明wchar_t是一个足够宽的变量类型,可以表示字符集中的任意一个字符)
Unicode 是一套字符集,而不是一套字符编码,严格来说,字符集和字符编码不是一个概念:
字符集定义了字符和二进制的对应关系,为每个字符分配了唯一的编号。可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,
计算机显示文字或者存储文字,就是一个查表的过程。
而字符编码规定了如何将字符的编号存储到计算机中。如果使用了类似 GB2312 和 GBK 的变长存储方案(不同的字符占用的字节数不一样),那么为了区分一个字符
到底使用了几个字节,就不能将字符的编号直接存储到计算机中,字符编号在存储之前必须要经过转换,在读取时还要再逆向转换一次,这套转换方案就叫做字符编码
Unicode最长是32位,也就是4个字节,因为UTF-8是1~6个字节来存储,当使用5或6字节存储时,就不属于Unicode编码了
宽字符输出函数
wprintf
wprintf无非就是printf的一个变种,和fprintf差不多只是格式上稍有区别
运行结果:
上面的代码中,wprintf使用的格式控制符是%ls,%ls意味着将对应的参数会被当作基于宽字符的字符串(wide chraracter string )看待,而%s则意味着对应的参数会被当作普通字符串(multi-byte string)看待,
不要因为上面一句话而错误的认为%s只用于printf,而%ls只用于wprintf,其实在windows下使用
和
都是可以正常输出宽字符串的(其他操作系统下就不一定了)
%s
当使用 printf() 时,按照单字符格式输出字符串
当使用 wprintf() 时,按照宽字符(两字节)格式输出字符串
%S
当使用 printf() 时,按照宽字符格式输出字符串
当使用 wprintf() 时,按照单字符格式输出字符串
注意这个H是宽字符串mm中的H,而不是ss中的H,ss中的字符串中的每个字符占一个字节,printf如果按照宽字符的标准来输出就无法正常输出了,而wprintf为什么只输出了H呢,不是输出字符串吗,
下面我们用VS来看宽字符在内存中的存储
从上图就可以很清楚的看出“Hello World”这个宽字符串在内存中的存储情况了,因为是宽字符所以大写字母H用两个字节表示(48 00),48是16进制转成10进制就是72,刚好就是'H'的ASCII码值的大小,如果按照单字符格式输出(也就是一个字节一个字节的输出)就输出H,继续往后,编译器看到第二个字节00,就以为字符串已经到结束了,最后我们看到的结果就是只输出了大写字符H
(本来对%S没有什么疑问,就当成一个格式控制符记住就是,后来在微软的官方文档里找到了%S这个参数的解释,如下图)
上面这段话的意思大概是,%S这个格式说明符,表示使用与函数支持的默认宽度“相反”的字符宽度,有了这一段话,上面的就很好解释了,printf因为默认支持的宽度是单字符,而%S偏要使用相反的,那么就
使用宽字符格式输出,而wprintf默认支持的宽度是宽字节,%S偏要使用相反的,意思就是使用单字符格式输出,这样记起来就容易多了
如果想要输出宽字符的单个字符,需要使用格式控制符%lc
(要清楚%ls和%s的意义在于指明的参数是何种字符串,而printf和wprintf的区别在于所使用的是不同类型的stream,不要混用 char 和 wchar_t 版本的流操作函数,否则会导致这些函数运行异常)
putwchar
putwchar函数专门用来输出一个宽字符,它和 putchar 的用法类似
wchar_t ch = L'Z';
putwchar(ch);
运行结果: