专栏导航
上一篇:第2章 :Unicode 由来简述
回到目录
下一篇:无
本节前言
本节,我们来讲解 char 数据类型与宽字符。在上一节,我们说了,在我们的专栏里,宽字符与 Unicode 字符,我们将它俩看作是同义语。
说是要来讲解这两种数据类型,实际上呢,我们真正想要让大家理解的东西,还是 Unicode 字符的概念。
一. 英特尔处理器的小端字节序
这个呢,其实是一种额外的知识点。不难,相信大家能学会。
我们来给出下面的一段代码。
unsigned int num = 0x10203040;
unsigned char * p = (char*)#
在上面的代码里,我们将指针 p 的值,设置为 unsigned int 型变量 num 的地址。不过呢,p 不是无符号整型指针,而是无符号字符型指针。
在这样的基础上,我们来想一想,p[0],p[1],p[2],p[3] 里面分别存储着什么。
可以采取两种办法来 num 的值 0x10203040 。
第一种呢,p[0] = 0x10,p[1] = 0x20,p[2] = 0x30, p[3] = 0x40 。
第二种,p[0] = 0x40,p[1] = 0x30,p[2] = 0x20, p[3] = 0x10 。
不论是哪一种方式,显然地,p[0] 的地址小于 p[1] 的地址。也就是,p[0],p[2],p[2],p[3] 之中,p[0] 位于低地址,p[3] 位于高地址。
第一种存储方式的思路是,存储某一个数的时候,高位字节存储在低地址里面,低位字节存储在高地址里面。这种存储方式,叫做大端字节序,或者叫做大尾数。
第二种存储方式的思路是,存储某一个数的时候,低位字节存储在低地址里面,高位字节存储在高地址里面。这种存储方式,叫做小端字节序,也叫做小尾数。
英特尔处理器,采用的,便是小端字节序。
也就是说,我们无论是使用 C 语言,还是使用 C++,C#,在某一个多字节的变量里面存储数据的时候,都是将低位字节存储在低地址里面,将高位字节存储在高地址里面。
有了这样的基础知识以后,我们接着往下学习。
二. char 型字符与 Unicode 字符
Unicode 字符,或者说,宽字符,它是基于 wchar_t 类型的。这个数据类型被定义在多个头文件之中。其中的一个头文件,便是 Wchar.h 头文件。定义代码如下所示。
typedef unsigned short wchar_t;
因此,wchar_t 数据类型和无符号短整型一样,都是 16 位宽。
与 wchar_t 相比,char 型与 unsigned char 型的长度就短了一些,它是 8 位宽。
下面的代码分别用 char 型变量和 wchar_t 型变量来存储单个字符。
char ch;
wchar_t wch;
ch = 'a';
wch = 'a';
上面的代码,分别用 char 型变量 ch 和 wchar_t 型变量 wch 存储了字符 'a' 的编码。
字符 'a' 的ASCII 码为 0x61,Unicode 字符集中,对应于英文字符的部分,与 ASCII 码是相同的。所以呢,char 型变量里面,ch 的值为 0x61 。wchar_t 型变量里面,wch 的值也是 0x61 。
只是,由于 char 型变量占据 8 位,而 wchar_t 型变量占用 16 位,所以呢,ch 里面的值为 0x61,而 wch 里面的值为 0x0061 。由于英特尔处理器是小端字节序,所以呢,在内存里面,wch 的存储方式为:0x61,0x00 。
对一个 wchar_t 型的变量赋予某一个英文字符的时候,你当然可以像上面的代码块中所示的那样子,直接将某一个字符用一对单引号括起来,然后将其赋给等号左边的 wchar_t 类型的变量。
然而,为了严谨一些,你也可以在左边的单引号之前,加上一个大写字母 L 。如下面的代码所示。
wchar_t wch;
wch = L't';
注意,在书写,大写字母 L 与左单引号之间,没有空格,是紧挨着的。
加上了 L 前缀之后的字符,被解释为宽字符,占用 16 位。不加的话,那么,它就是一个 8 位的字符了。
在你自己的代码中,你可以给一个宽字符变量赋值的时候,L 前缀可以加,也可以不加。
这是因为,如果不加的话,无非就是将一个普通的 char 型字符的 ASCII 码,赋给一个 unsigned short 类型的变量而已。编译器会进行自动类型转换,在转换的时候,会自动将 char 类型的数据扩充为 unsigned short 类型的数据。
到了这里,char 型字符与宽字符,我们就算是讲完了。
三. char 型字符串与 Unicode 字符串
我们看下面的代码。
char str[] = "Hello";
wchar_t wstr[] = L"Hello";
如上面的代码块所示,我们在对 char 型字符串进行初始化的时候,我们用的是不加 L 前缀的 "Hello" 。而在对 Unicode 字符串进行初始化的时候,我们用的是加上 L 前缀的 "Hello" 。
在对字符串进行初始化的时候,前缀 L 就必须要认真地对待了。给 char 型字符串初始化的时候,一定不要加 L 前缀。而在给 Unicode 字符串初始化的时候,一定要加上 L 前缀。
这是因为,如果加上了 L 前缀,这就相同于告诉了编译器,我们想要用两个字节来表示字符串中的每一个字符。若是不加 L 前缀,这就相同于告诉了编译器,我们想要用一个字节来表示字符串中的每一个字符。
也就是,str 字符串的内存排布方式为:'H','e','l','l','o','\0' 。
而 wstr 字符串的内存排布方式为:'H','\0','e','\0','l','\0','l','\0','o','\0','\0','\0' 。
以上两种字符串的排布方式中,每一个小片段,都是 1 字节。
str[1] 里面是字符 'e',或者 ASCII 码 0x65 。而 wstr[1]里面,存储的是 0x0065 。也就是说,char 型字符数组里面的每一个元素,都是一个单字节的字符的 ASCII 码。而 Unicode 字符数组里面的每一个元素,都是一个 2 字节的宽字符的 ASCII 码。
sizeof(str) 的结果为 6,而 sizeof(wstr) 的结果为 12 。给 sizeof 操作符里面传递一个 数组名参数,则返回的,会是这个数组的字节尺寸。对于未指定长度的字符数组来讲,它是会包含结尾的 NUL 字符的。
就目前的学习阶段来讲,如果大家给宽字符串,或者 Unicode 字符串进行初始化的时候,千万不要忘记添加 L 前缀。
结束语
本节的内容就这么多。多看几遍,你应该是能看懂的。
专栏导航
上一篇:第2章 :Unicode 由来简述
回到目录
下一篇:无