L

        最近在网上无意中看到了这样一句代码:
wchar_t text[] = L"Hello World!";

        看着字符串前那个并不算醒目的’L’,一时间僵在了那里。这是个什么用法?用了这么多年C语言没见过啊。这个和不带L定义出来的字符串有什么不同?还有那个wchar_t是个什么特殊类型?

        众多疑问就像一个个二踢脚一样“嗖嗖嗖”地蹿向我的大脑。事不宜迟,赶紧带着这些二踢脚,哦不不,是疑问,领着那段代码,跑到自己的电脑中去试一试吧。

#include <stdio.h>

int main(void)
{
    wchar_t text[] = L"Hello World!";

    printf("%s\n", text);

    return 0;
}

        这段程序的输出结果是“H”(某些看官不要浮想联翩啊 ★_★),只输出了整个字符串的第一个字符。额,看来真是与不带L定义出的字符串不同。既然不同,就把这个L删掉试试,再次编译,

        error:wchar_t-array initialized from non-wide string

        得,这回连编译都过不了了。不过亮点来了,“non-wide string”这又是个什么东西。对了,前面还有个wchar_t,把它换成普通的char试试。于是把L加回去,wchar_t变为char,再次编译,

        error:char-array initialized from wide string

        额,再次报错。不过亮点真的确定是亮点了,“wide string”和“non-wide string”。依旧事不宜迟,带着这两个亮点(虽然还差一个吧★_★),奔向Internet那浩瀚的海洋中……

        经过一番查阅,终于大致了解了这L的作用,以及“wide string”和“non-wide string”的含义。那么接下来,且听我慢慢道来。

        故事要从侏罗纪时期,额,也没那么远吧,石器时代、蒸汽时代、电气时代、额,也就差不多信息技术时代吧,对,也就是计算机发明之后不久的那段时间哈。为了在计算机中表示字符,人们制定了ASCII编码。可这8bits的ASCII编码也就能表示256个字符(0x00~0xFF),英文字母abcd…,数字0123什么的,再加上制表符,结束符乱七八糟的控制字符是够了,可其它国家的文字字符怎么办?中文怎么办、尼日利亚语怎么办、吉尔吉吉斯坦语怎么办、非洲土著语(得考证一下他们用不用计算机哈★_★)怎么办?基于这些现实问题,人们又制定了UNICODE标准字符集。UNICODE使用2个字节表示一个字符,其范围从0x0000~0xFFFF共65536个字符(其中汉字就占了4万多个),这样全世界的语言中的常用文字的字符就都可以包含在其中了。由于UNICODE使用2个字节编码一个字符,所以其也被称为“宽字节编码”。诶,这位看官问了,为什么不叫“双字节编码”?额,您要愿意这么叫也成,反正编译器不这么叫,嘿嘿。

        那么之前程序中的L也就是告诉编译器,之后的字符串使用宽字节编码方式,也就是“wide string”。同时,像ASCII这种使用单字节编码的编码方式,也就是“non-wide string”的一种了。wchar_t在C语言中一般定义为unsigned short,32位机上2个字节,64位机上4个字节,总之都能存储宽字节编码。而之前的程序只打印出字符串的第一个字符,就是因为使用单字节打印模式打印宽字节造成的,这个在之后的程序中会进行说明。

        注:字符串或字符前加L只是告诉编译器字符串或字符采用宽字节编码表示一个字符,具体使用什么类型的编码(比如GBK、GB2312、阿拉伯语……)是通过setlocale()指定的。

        至此,L啊,H啊,额不对,没有H,都揭开了面纱。那么接下来用程序来验证一下。

 

        先说一下我这儿的环境:

$ uname -rvmiops
Linux 2.6.18-128.el5 #1 SMP Sat Feb 7 15:21:36 CST 2009 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -v
Using built-in specs.
Target: x86_64-turbo-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-turbo-linux
Thread model: posix
gcc version 4.1.2 20071124 (Turbolinux 4.1.2-42)

        程序如下:

#include <stdio.h>

int main(void)
{
    int i = 0;
    char *p = NULL;

    char text[] = "Hello World!";
    wchar_t text_l[] = L"Hello World!";

    printf("sizeof(text) = %ld, sizeof(text_l) = %ld.\n", sizeof(text), sizeof(text_l));

    p = (char *)text;
    for(i = 0; i < sizeof(text); ++i)
    {
        printf("text[%d] = 0x%02X.\n", i, *(p++));
    }

    p = (char *)text_l;
    for(i = 0; i < sizeof(text_l); ++i)
    {
        printf("text_l[%d] = 0x%02X.\n", i, *(p++));
    }

    printf("text = %s.\ntext_l = %ls.\n", text, text_l);

    return 0;
}

        程序的运行结果如下:

sizeof(text) = 13, sizeof(text_l) = 52.
text[0] = 0x48.
text[1] = 0x65.
text[2] = 0x6C.
text[3] = 0x6C.
text[4] = 0x6F.
text[5] = 0x20.
text[6] = 0x57.
text[7] = 0x6F.
text[8] = 0x72.
text[9] = 0x6C.
text[10] = 0x64.
text[11] = 0x21.
text[12] = 0x00.
text_l[0] = 0x48.
text_l[1] = 0x00.
text_l[2] = 0x00.
text_l[3] = 0x00.
text_l[4] = 0x65.
text_l[5] = 0x00.
text_l[6] = 0x00.
text_l[7] = 0x00.
text_l[8] = 0x6C.
text_l[9] = 0x00.
text_l[10] = 0x00.
text_l[11] = 0x00.
text_l[12] = 0x6C.
text_l[13] = 0x00.
text_l[14] = 0x00.
text_l[15] = 0x00.
text_l[16] = 0x6F.
text_l[17] = 0x00.
text_l[18] = 0x00.
text_l[19] = 0x00.
text_l[20] = 0x20.
text_l[21] = 0x00.
text_l[22] = 0x00.
text_l[23] = 0x00.
text_l[24] = 0x57.
text_l[25] = 0x00.
text_l[26] = 0x00.
text_l[27] = 0x00.
text_l[28] = 0x6F.
text_l[29] = 0x00.
text_l[30] = 0x00.
text_l[31] = 0x00.
text_l[32] = 0x72.
text_l[33] = 0x00.
text_l[34] = 0x00.
text_l[35] = 0x00.
text_l[36] = 0x6C.
text_l[37] = 0x00.
text_l[38] = 0x00.
text_l[39] = 0x00.
text_l[40] = 0x64.
text_l[41] = 0x00.
text_l[42] = 0x00.
text_l[43] = 0x00.
text_l[44] = 0x21.
text_l[45] = 0x00.
text_l[46] = 0x00.
text_l[47] = 0x00.
text_l[48] = 0x00.
text_l[49] = 0x00.
text_l[50] = 0x00.
text_l[51] = 0x00.
text = Hello World!.
text_l = Hello World!.

        简要说明一下,因为我这里是64位机器,所以unsigned short占4Bytes,即wchar_t占4Bytes。所以输出结果的第一行,一个有13Bytes的char型字符串在wchar_t类型下就占了52Bytes。接下来,我将字符串在内存中存储的值逐个打印了出来。可以看到,char型每个字符占1Byte,而wchar_t每个字符占4Bytes。4字节中第一个字节的值对应于ASCII码中英文字符的编码值,后面的3个字节都是0x00,对应于ASCII码的字符串结束符。所以使用单字节模式(即%s)打印宽字节时,就只打印出了字符串的第一个字符。而如若想要打印宽字节,应使用%ls。

        至此,之前的疑惑都有了答案,那些蹿出来的二踢脚也都“咚咚咚”的炸开了。打完收工,酣畅淋漓。


你可能感兴趣的:(L)