Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1466721
  • 博文数量: 218
  • 博客积分: 6394
  • 博客等级: 准将
  • 技术积分: 2563
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-08 15:33
个人简介

持之以恒

文章分类

全部博文(218)

文章存档

2013年(8)

2012年(2)

2011年(21)

2010年(55)

2009年(116)

2008年(16)

分类: WINDOWS

2009-05-17 23:29:28

ASCII字符--Char类型

下面的语句定义并初始化了一个只包含一个字符的变量:
char c = 'A' ;
变量c需要1个字节(8位)来保存,并将用十六进位数0x41初始化,这是字母A的ASCII代码。
可以这样定义一个指向字符串的指针:
char * p ;
因为Windows是一个32位操作系统,所以指针变量p需要用4个字节保存。
初始化一个指向字符串的指针:

char * p = "Hello!" ;

像前面一样,变量p也需要用4个字节保存。该字符串保存在静态内存中占用7个字节来保存6个字节的字符串,另1个字节保存终止符号’\0’。
您还可以像这样定义字符数组:
char a[10] ;
在这种情况下,编译器为该数组保留了10个字节的储存空间。运算式sizeof(a) 将返回10。(注意sizeof返回的是变量在内存中占用的字节数,而不是字符的个数)
还有两种定义字符数组的方式:

char a[] = "Hello!" ;

static char a[] = "Hello!" ;

无论哪种情况,字符串都储存在静态程序内存中,并在末尾添加0,这样就需要7个字节的储存空间。(sizeof(a) 的值为 7)

—WChar_t类型

MSDN上关于Unicode的说法

Unicode is a specification for supporting all character sets, including character sets that cannot be represented in a single byte. If you are programming for an international market, consider using either Unicode or (MBCSs) or enabling your program so you can build it for either by changing a switch.

A wide character is a 2-byte multilingual character code. Most characters used in modern computing worldwide, including technical symbols and special publishing characters, can be represented according to the Unicode specification as a wide character.

A wide-character string is represented as a wchar_t[] array and is pointed to by a wchar_t*prefixing the letter L to the character. For example, L'\0' is the terminating wide (16-bit) NULL character. Similarly, any ASCII string literal can be represented as a wide-character string literal by prefixing the letter L to the ASCII literal (L"Hello").

Unicode或者宽字符都没有改变char类型在C中的含义。char继续表示1个字节的储存空间, sizeof (char) 继续返回1。
C中的宽字符基于wchar_t类型的,它在几个表头文件包括WCHAR.H中都有定义,像这样:

typedef unsigned short wchar_t ;

因此,wchar_t类型与无符号短整数型态相同,都是16位宽。
要定义包含一个宽字符的变数,可使用下面的语句:
wchar_t c = 'A' ;
变数c是一个双字节值0x0041,是Unicode表示的字母A
还可定义指向宽字符串的指针:

wchar_t * p = L"Hello!" ;//指针变数p要占用4个字节,而字符串变数需要14个字节-每个字符需要2个字//节,末尾的0还需要2个字节。

注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。
用下面的语句定义宽字符数组:

static wchar_t a[] = L"Hello!" ;

该字符串也需要14个字节的储存空间,sizeof (a) 将返回14。索引数组a可得到单独的字符。a[1] 的值是宽字符「e」,或者0x0065。
虽然看上去更像一个印刷符号,但第一个引号前面的L非常重要,并且在两个符号之间必须没有空格。只有带有L,编译器才知道您需要将字符串存为每个字符2字节。
您还可在单个字符文字前面使用L字首,来表示它们应解释为宽字符。如下所示:
wchar_t c = L'A' ;
但通常这是不必要的,C编译器会对该字符进行扩充,使它成为宽字符。

宽字符程序库函数

我们都知道如何获得字符串的长度。例如,如果我们已经像下面这样定义了一个字符串指针:

char * pc = "Hello!" ;

我们可以调用
iLength = strlen (pc) ;
这时变数iLength将等于6,也就是字符串中的字符数。
太好了!现在让我们试着定义一个指向宽字符的指针:

wchar_t * pw = L"Hello!" ;

再次调用strlen :
iLength = strlen (pw) ;
现在麻烦来了。首先,C编译器会显示一条警告消息,可能是这样的内容:

'function' : incompatible types - from 'unsigned short *' to 'const char *'

这条消息的意思是:声明strlen函数时,该函数应接收char类型的指针,但它现在却接收了一个unsigned short类型的指针。您仍然可编译并执行该程序,但您会发现iLength等于1。为什么?
字符串「Hello!」中的6个字符占用16位:

0x0048 0x0065 0x006C 0x006C 0x006F 0x0021

Intel处理器在内存中将其存为:

48 00 65 00 6C 00 6C 00 6F 00 21 00

假定strlen函数正试图得到一个字符串的长度,并把第1个字节作为字符开始计数,但接著假定如果下一个字节是0,则表示字符串结束。
这个小练习清楚地说明了C语言本身和执行时期程序库函数之间的区别。编译器将字符串L"Hello!" 解释为一组16位短整数型态资料,并将其保存在wchar_t数组中。编译器还处理数组索引和sizeof操作符(估计这两个函数是内联函数~~),因此这些都能正常工作,但在连结时才添加执行时期程序库函数(连接器的工作),例如strlen。这些函数认为字符串由单字节字符组成。遇到宽字符串时,函数就不像我们所希望那样执行了。
您可能要说:「噢,太麻烦了!」现在每个C语言程序库函数都必须重写以接受宽字符。但事实上并不是每个C语言程序库函数都需要重写,只是那些有字符串参数的函数才需要重写,而且也不用由您来完成。它们已经重写完了。

strlen函数的宽字符版是wcslen(wide-character string length:宽字符串长度),并且在STRING.H(其中也说明了strlen)和WCHAR.H中均有说明。strlen函数说明如下:

size_t __cdecl strlen (const char *) ;

而wcslen函数则说明如下:

size_t __cdecl wcslen (const wchar_t *) ;

这时我们知道,要得到宽字符串的长度可以调用
iLength = wcslen (pw) ;
函数将返回字符串中的字符数6。请记住,改成宽字节后,字符串的字符长度(字符的个数)不改变,只是字节长度改变了。
您熟悉的所有带有字符串参数的C执行时期程序库函数都有宽字符版。例如,wprintf是printf的宽字符版。这些函数在WCHAR.H和含有标准函数说明的表头文件中说明。

维护单一代码—TCHAR类型

The TCHAR data type is a Win32 character string that can be used to describe ANSI, double-byte character set (DBCS), or Unicode strings. For ANSI and DBCS platforms, TCHAR is defined as shown in the following Syntax section. For Unicode platforms, TCHAR is defined as synonymous with the WCHAR type.

Syntax

typedef char     TCHAR;

使用Unicode的缺点:
程序中的每个字符串都将占用两倍的储存空间。
宽字符执行时期程序库中的函数比常规的函数大。
建立两个版本的程序:
一个处理ASCII字符串,另一个处理Unicode字符串。这样的话是很麻烦的!
最好的解决办法是维护既能按ASCII编译又能按Unicode编译的单一源文件。

一个办法是使用Microsoft Visual C++包含的TCHAR.H表头文件。该表头文件不是ANSI C标准的一部分,因此那里定义的每个函数和宏定义的前面都有一条下划线。TCHAR.H为需要字符串参数的标准执行时期程序库函数提供了一系列的替代名称(例如,_tprintf和_tcslen)。有时这些名称也称为通用函数名称,因为它们既可以指向函数的Unicode版也可以指向非Unicode版。

如果定义了名为_UNICODE的识别字,并且程序中包含了TCHAR.H表头文件,那么_tcslen就定义为wcslen:
#define _tcslen wcslen
如果没有定义UNICODE,则_tcslen定义为strlen:
#define _tcslen strlen

等等。TCHAR.H还用一个新的资料型态TCHAR来解决两种字符资料型态的问题。如果定义了 _UNICODE识别字,那么TCHAR就是wchar_t:

typedef wchar_t TCHAR ;
否则,TCHAR就是char:
typedef char TCHAR ;
现在开始讨论字符串文字中的L问题。如果定义了_UNICODE识别字,那么一个称作__T的宏就定义如下:
#define __T(x) L##x
这是相当晦涩的语法,但合乎ANSI C标准的前置处理器规范。那一对井字号称为「粘贴符号(token paste)」,它将字母L添加到宏参数上。因此,如果宏参数是"Hello!",则L##x就是L"Hello!"。
如果没有定义_UNICODE识别字,则__T宏只简单地定义如下:
#define __T(x) x
此外,还有两个宏与__T定义相同:
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
在Win32 console程序中使用哪个宏,取决于您喜欢简洁还是详细。基本地,必须按下述方法在_T或_TEXT宏内定义字符串文字:

_TEXT ("Hello!")

这样做的话,如果定义了_UNICODE,那么该串将解释为宽字符的组合,否则解释为8位的字符字符串。

Windows表头文件类型

一个Windows程序包括表头文件WINDOWS.H。该文件包括许多其它表头文件,包括WINDEF.H,该文件中有许多在Windows中使用的基本型态定义,而且它本身也包括WINNT.H。WINNT.H处理基本的Unicode支援。
WINNT.H的前面包含C的表头文件CTYPE.H,这是C的众多表头文件之一,包括wchar_t的定义。WINNT.H定义了新的资料型态,称作CHAR和WCHAR:
typedef char CHAR ;

typedef wchar_t WCHAR ;       // wc

当您需要定义8位字符或者16位字符时,推荐您在Windows程序中使用的资料型态是CHAR和WCHAR。WCHAR定义后面的注释是匈牙利标记法的建议:一个基于WCHAR资料型态的变数可在前面附加上字母wc以说明一个宽字符。
WINNT.H表头文件进而定义了可用做8位字符串指针的六种资料型态和四个可用做const 8位字符串指针的资料型态。这里精选了表头文件中一些实用的说明类型语句:

typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;

typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR ;

字首N和L表示「near」和「long」,指的是16位Windows中两种大小不同的指针。在Win32中near和long指针没有区别。
类似地,WINNT.H定义了六种可作为16位字符串指针的资料型态和四种可作为const 16位字符串指针的资料型态:

typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ;

typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;

至此,我们有了资料型态CHAR(一个8位的char)和WCHAR(一个16位的wchar_t),以及指向CHAR和WCHAR的指针。与TCHAR.H一样,WINNT.H将TCHAR定义为一般的字符类型。如果定义了识别字UNICODE(没有底线),则TCHAR和指向TCHAR的指针就分别定义为WCHAR和指向WCHAR的指针;如果没有定义识别字UNICODE,则TCHAR和指向TCHAR的指针就分别定义为char和指向char的指针:

#ifdef  UNICODE                  

typedef WCHAR TCHAR, * PTCHAR ;

typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ;

typedef LPCWSTR LPCTSTR ;
#else

typedef char TCHAR, * PTCHAR ;

typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ;

typedef LPCSTR LPCTSTR ;
#endif
如果已经在某个表头文件或者其它表头文件中定义了TCHAR类型,那么WINNT.H和WCHAR.H表头文件都能防止其重复定义。不过,无论何时在程序中使用其它表头文件时,都应在所有其它表头文件之前包含WINDOWS.H。
WINNT.H表头文件还定义了一个宏,该宏将L添加到字符串的第一个引号前。如果定义了UNICODE识别字,则一个称作 __TEXT的宏定义如下:

#define __TEXT(quote) L##quote

如果没有定义识别字UNICODE,则像这样定义__TEXT宏:
#define __TEXT(quote) quote
此外, TEXT宏可这样定义:

#define TEXT(quote) __TEXT(quote)

这与TCHAR.H中定义_TEXT宏的方法一样,只是不必操心底线。我将在本书中使用这个宏的TEXT版本。
这些定义可使您在同一程序中混合使用ASCII和Unicode字符串,或者编写一个可被ASCII或Unicode编译的程序。如果您希望明确定义8位字符变数和字符串,请使用CHAR、PCHAR(或者其它),以及带引号的字符串。为明确地使用16位字符变数和字符串,请使用WCHAR、PWCHAR,并将L添加到引号前面。对于是8位还是16位取决于UNICODE识别字的定义的变数或字符串,要使用TCHAR、PTCHAR和TEXT宏。

Windows的字符串函数

正如前面谈到的,Microsoft C包括宽字符和需要字符串参数的C语言执行时期程序库函数的所有普通版本。不过,Windows复制了其中一部分。例如,下面是Windows定义的一组字符串函数,这些函数用来计算字符串长度、复制字符串、连接字符串和比较字符串:

ILength = lstrlen (pString) ;

pString = lstrcpy (pString1, pString2) ;

pString = lstrcpyn (pString1, pString2, iCount) ;

pString = lstrcat (pString1, pString2) ;

iComp = lstrcmp (pString1, pString2) ;

iComp = lstrcmpi (pString1, pString2) ;

这些函数与C程序库中对应的函数功能相同。如果定义了UNICODE识别字,那么这些函数将接受宽字符串,否则只接受常规字符串。 

MFC中的CString

CString is based on the TCHAR data type. If the symbol _UNICODE is defined for a build of yourprogram, TCHAR is defined as type wchar_t, a 16-bit character encoding type; otherwise, it is defined as char, the normal 8-bit character encoding. Under Unicode, then, CStrings are composed of 16-bit characters. Without Unicode, they are composed of characters of type char.

MFC Support for Unicode Strings

Use the _T macro to conditionally code literal strings to be portable to Unicode.

When you pass strings, pay attention to whether function arguments require a length in characters or a length in bytes. The difference is important if you're using Unicode strings.

Use portable versions of the C run-time string-handling functions.

Use the following data types for characters and character pointers:

TCHAR   Where you would use char.
LPTSTR   Where you would use char*.

LPCTSTR   Where you would use const char*. CString provides the operator LPCTSTR to convert between CString and LPCTSTR.

MFC Support for MBCS Strings

· The class library is also enabled for multibyte character sets — specifically for double-byte character sets (DBCS).

· Under this scheme, a character can be either one or two bytes wide. If it is two bytes wide, its first byte is a special "lead byte," chosen from a particular range depending on which code page is in use. Taken together, the lead and "trail bytes" specify a unique character encoding.

· If the symbol _MBCS is defined for a build of your program, type TCHAR, on which CString is based, maps to char. It's up to you to determine which bytes in a CString are lead bytes and which are trail bytes. The C run-time library supplies functions to help you determine this.

· Under DBCS, a given string can contain all single-byte ANSI characters, all double-byte characters, or a combination of the two. These possibilities require special care in parsing strings, including CString objects.

By definition, the ASCII character set is a subset of all multibyte-character sets. In many multibyte character sets, each character in the range 0x00 – 0x7F is identical to the character that has the same value in the ASCII character set. For example, in both ASCII and MBCS character strings, the 1-byte NULL character ('\0') has value 0x00 and indicates the terminating null character.
关键字:char、TCHAR、wchar_t、CString、UNICODE、MBCS

     char cTest[] = "学习CString";

     int cLength = sizeof(cTest);//12(4+7+1)

     CString csTest = _T("学习CString");

     int csLength = csTest.GetLength();//9(字符的个数)

     int csConvertLength = ::WideCharToMultiByte(CP_ACP,0,csTest,-1,NULL,0,NULL,0);//12

     char * cConvertChar = new char[csConvertLength];

     ::WideCharToMultiByte(CP_ACP,0,csTest,-1,cConvertChar,csConvertLength,NULL,0);

     wchar_t wcTest[] = L"学习CString";

     int wcLength = sizeof(wcTest);//20((9 + 1)* 2)

     TCHAR  tTest1[]= _T("学习CString");

     TCHAR tTest2[] = _T("CString");

     int tLength1 = sizeof(tTest1);//20((9 + 1)* 2)

     int tLength2  = sizeof(tTest2);//16(8*2)
阅读(2879) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~