对C++ Local的经典分析
本贴转载自:再别流年的技术实验室
文章地址: http://kittsoft.xp3.biz/?p=86
“这个问题比你想象中复杂”
(我也学下BS的风格,虽然这句话是我自己临时想说的。^^)
从字符到整数
char是一种整数类型,这句话的含义是,char所能表示的字符在C/C++中都是整数类型。好,接下来,很多文章就会举出一个典型例子,比如,’a' 的数值就是0×61。这种说法对吗?如果你细心的读过K&R和BS对于C和C++描述的原著,你就会马上反驳道,0×61只是’a'的ASCII 值,并没有任何规定C/C++的char值必须对应ASCII。C/C++甚至没有规定char占几位,只是规定了sizeof(char)等于1。
当然,目前大部分情况下,char是8位的,并且,在ASCII范围内的值,与ASCII对应。
本地化策略集(locale)
“将’a'翻译成0×61的整数值”,“将ASCII范围内的编码与char的整数值对应起来”,类似这样的规定,是特定系统和特定编译器制定 的,C/C++中有个特定的名词来描述这种规定的集合:本地化策略集(locale。也有翻译成“现场”)。而翻译——也就是代码转换(codecvt) 只是这个集合中的一个,C++中定义为策略(facet。也有翻译为“刻面”)
C/C++的编译策略
“本地化策略集”是个很好的概念,可惜在字符和字符串这个层面上,C/C++并不使用(C++的locale通常只是影响流(stream)),C/C++使用更直接简单的策略:硬编码。
简单的说,字符(串)在程序文件(可执行文件,非源文件)中的表示,与在程序执行中在内存中的表示一致。考虑两种情况:
A、char c = 0×61;
B、char c = ‘a’;
情况A下,编译器可以直接认识作为整数的c,但是在情况B下,编译器必须将’a'翻译成整数。编译器的策略也很简单,就是直接读取字符(串)在源文件中的编码数值。比如:
const char* s = “中文abc”;
这段字符串在GB2312(Windows 936),也就是我们的windows默认中文系统源文件中的编码为:
0xD6 0xD0 0xCE 0xC4 0×61 0×62 0×63
在UTF-8,也就是Linux默认系统源文件中的编码为:
0xE4 0xB8 0xAD 0xE6 0×96 0×87 0×61 0×62 0×63
一般情况下,编译器会忠实于源文件的编码为s赋值,例外的情况比如VC会自作聪明的把大部分其他类型编码的字符串转换成GB2312(除了像UTF-8 without signature这样的幸存者)。
程序在执行的时候,s也就保持是这样的编码,不会再做其他的转换。
宽字符 wchar_t
正如char没有规定大小,wchar_t同样没有标准限定,标准只是要求一个wchar_t可以表示任何系统所能认识的字符,在win32 中,wchar_t为16位;Linux中是32位。wchar_t同样没有规定编码,因为Unicode的概念我们后面才解释,所以这里只是提一下,在 win32中,wchar_t的编码是UCS-2BE;而Linux中是UTF-32BE(等价于UCS-4BE),不过简单的说,在16位以内,一个字 符的这3种编码值是一样的。因此:
const wchar_t* ws = L”中文abc”;
的编码分别为:
0x4E2D 0×6587 0×0061 0×0062 0×0063 //win32,16位
0x00004E2D 0×00006587 0×00000061 0×00000062 0×00000063 //Linux,32位
大写的L是告诉编译器:这是宽字符串。所以,这时候是需要编译器根据locale来进行翻译的。
比如,在Windows环境中,编译器的翻译策略是GB2312到UCS-2BE;Linux环境中的策略是UTF-8到UTF-32BE。
这时候就要求源文件的编码与编译器的本地化策略集中代码翻译的策略一致,例如VC只能读取GB2312的源代码(这里还是例外,VC太自作聪明了 ,会将很多其他代码在编译时自动转换成GB2312),而gcc只能读取UTF-8的源代码(这里就有个尴尬,MinGW运行win32下,所以只有 GB2312系统才认;而MinGW却用gcc编写,所以自己只认UTF-8,所以结果就是,MinGW的宽字符被废掉了)。
宽字符(串)由编译器翻译,还是被硬编码进程序文件中。
Unicode和UCS
Unicode和UCS是两个独立的组织分别制定的一套编码标准,但是因为历史的原因,这两套标准是完全一样的。Unicode这个词用得比较多的原因可 能是因为比较容易记住,如果没有特别的声明,在本文所提及的Unicode和UCS就是一个意思。Unicode的目标是建立一套可以包含人类所有语言文 字符号你想得到想不到的各种东西的编码,其编码容量甚至预留了火星语以及银河系以外语言的空间——开个玩笑,反正简单的说,Unicode编码集足够的 大,如果用计算机单位来表示,其数量比3个字节大一些,不到4个字节。
Unicode和UTF
因为Unicode包含的内容太多,其编码在计算机中的表示方法就成为了一个有必要研究的问题。传统编码,比如标准的7位ASCII,在计算机中的表示方 法就是占一个字节的后7位,这似乎是不需要解释就符合大家习惯的表示方法。但是当今Unicode的总数达到32位(计算机的最小单位是字节,所以大于3 字节,就只能至少用4字节表示),对于大部分常用字符,比如Unicode编码只占一个字节大小的英语字母,占两个字节大小汉字,都用4个字节来储存太奢 侈了。另外,如果都用4字节直接表示,就不可避免的出现为0的字节。而我们知道,在C语言中,0×00的字节就是’