唯有前进值得敬仰

---等得越久,相聚时越幸福
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

宽字符变量 L

Posted on 2010-01-27 20:15  绿豆芽33  阅读(3002)  评论(1编辑  收藏  举报

经常看到define中定义字符串时,在字符串前面加上一个大写的字母L,例如:#define variable L"hello world"

这是干什么用的呢?

其实,这个L是宽字符,通知编译器后面的字符为UNICODE标准。这种标准和ANSI有什么区别呢?

8位ANSI字符 char
16位 UNICODE字符 wchar_t

在<tchar.h>中有 typedef unsigned short wchar_t;

声明UNICODE字符和字符串的方法:
wchar_t c=L'A';
wchar_t szBuffer[100]=L"A String";

下面的例子很清楚的说明了二者之间的区别,

//        56 43 D6 AA CA B6 BF E2 00             //char a[] = "VC知识库";
//        56 00 43 00 E5 77 C6 8B 93 5E 00 00    //wchar_t b[] = L"VC知识库";

前面是实际的十六进制编码,后面是两种编码标准,由此可见,一个UNICODE字符在内存中占两个字节,而一个ANSI字符占一个字节。

为使更详细,我转载了一位高人的博文。

【转】

The C++ Programming Language 3rd》中有这么两段话:

from 4.3:
A type wchar_t is provided to hold characters of a larger character set such as Unicode. It is a distinct type. The size of wchar_t is implementation-defined and large enough to hold the largest character set supported by the implementation’s locale (see §21.7, §C.3.3). The strange name is a leftover from C. In C, wchar_t is a typedef (§4.9.7) rather than a builtin type. The suffix _ t was added to distinguish standard typedefs.

from 4.3.1:
Wide character literals are of the form L′ab′, where the number of characters between the quotes and their meanings is implementation-defined to match the wchar_t type. A wide character literal has type wchar_t.

这两段话中有两个要点是我们关心的:

  1. wchar_t 的长度是由实现决定的;
  2. L"ab" 的含义是由实现决定的。

那么GNU g++和VC6.0/7.0各是怎么实现的呢?看下面代码:

//author: **.Zhou
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void prt( const void* padd, size_t n )
{
    const unsigned char* p = static_cast<const unsigned char*>( padd );
    const unsigned char* pe = p + n;
    for( ; p<pe; ++p ) printf( " %02X", *p ); printf( "\n" );
}

int main()
{
    char a[] = "VC知识库";
    wchar_t b[] = L"VC知识库";
    prt( a, sizeof(a) );
    prt( b, sizeof(b) );
    system( "Pause" );
    // 说明:
    //  Dev-CPP4990 显示为:
    //    56 43 D6 AA CA B6 BF E2 00
    //    56 00 43 00 D6 00 AA 00 CA 00 B6 00 BF 00 E2 00 00 00
    //  VC++6.0 和 VC.net2003 显示为:
    //    56 43 D6 AA CA B6 BF E2 00
    //    56 00 43 00 E5 77 C6 8B 93 5E 00 00
    // 可见,Dev-CPP中的L""不是unicode编码,只是简单的扩充,汉字需要4bytes存储
    
    HWND h = FindWindow( NULL, "计算器" );
    SetWindowTextA( h, a );
    system( "Pause" );
    SetWindowTextW( h, b );
    system( "Pause" );
    // 说明:
    //   VC++6.0 和 VC.net2003 都能成功将标题改为"VC知识库"
    //  而 Dev-CPP4990 只有 SetWindowTextA 显示正确,而 SetWindowTextW 显示的是乱码 
}      

  这段代码说明了,g++(Dev-CPP 用的是 MingGW 编译器)中 L"xx" 解释为把作为 non-wide-char 的 "xx" 扩展为作为 wide-char 的 wchar_t,不足则在高位补0;而 VC6.0 的 L"xx" 解释为把作为 MBCS 的 "xx" 转换为作为 unicode 的 WCHAR,目前的 MBCS 是以 char 为一个存储单元的,而 WCHAR 在 winnt.h 中定义为 typedef wchar_t WCHAR。在 Windows 平台上,只要是超过 0~127 范围内的 char 型字符,都被视为 MBCS,它由1到2个字节组成,MBCS 字符集跟它的地区代码页号有关。在某个特定的 Windows 平台,默认的代码页号可以在控制面板 -> 区域选项中设定。

关于上述结论可以有下面这个程序来验证:

//author: smileonce
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>

void prt( const void* padd, size_t n )
{
    const unsigned char* p = static_cast<const unsigned char*>( padd );
    const unsigned char* pe = p + n;
    for( ; p<pe; ++p ) printf( " %02X", *p ); printf( "\n" );
}

int main()
{
    char a[] = "VC知识库";
    wchar_t b[] = L"VC知识库";
    prt( a, sizeof(a) );
    prt( b, sizeof(b) );

    PSTR pMultiByteStr = (PSTR)a;
    PWSTR pWideCharStr;
    int nLenOfWideCharStr;

    // 利用API函数MultiByteToWideChar()来把a转化成unicode字符
    nLenOfWideCharStr = MultiByteToWideChar( CP_ACP, 0, pMultiByteStr, -1, NULL, 0);
    pWideCharStr = (PWSTR)HeapAlloc( GetProcessHeap(), 0, nLenOfWideCharStr * sizeof(WCHAR) );
    assert( pWideCharStr );
    MultiByteToWideChar( CP_ACP, 0, pMultiByteStr, -1, pWideCharStr, nLenOfWideCharStr );

    prt( pWideCharStr, nLenOfWideCharStr * sizeof(WCHAR) );

    system( "Pause" );
//    // 说明:
//        56 43 D6 AA CA B6 BF E2 00             //char a[] = "VC知识库";
//        56 00 43 00 E5 77 C6 8B 93 5E 00 00    //wchar_t b[] = L"VC知识库";
//        56 00 43 00 E5 77 C6 8B 93 5E 00 00    //用MultiByteToWideChar()把a转换为unicode
//    // 可见,b[]的字符代码就是unicode代码

    return 0;   
}      

呵呵,问题已经明了,总结一下:

  1. ISO C 中 wchar_t 是一个 typedef,ISO C++ 中 wchar_t 是语言内建的数据类型,L"xx" 是ISO C/C++ 语言内建的表示 wchar_t 的文本量的语法;
  2. wchar_t 的长度是由实现决定的;
  3. L"xx" 的意义是由实现决定的;
  4. 默认的 "xx" 是 non-wide-char,其每个元素数据的类型是 char;与其相对应的 L"xx" 是wide-char,其每个元素数据的类型是wchar_t。

  为什么 C/C++ 语言把 L"xx" 定义为由实现决定的呢?这显然是为了 C/C++ 的普适性、可移植性。Bjarne 的观点认为,C++ 的方式是允许程序员使用任何字符集作为串的字符类型。另外,unicode 编码已经发展了若干版本了,是否能永久适合下去也不得而知。有关 unicode 的详细论述以及和其它字符集的比较,我推荐你看《无废话xml》