代码改变世界

C语言Windows程序设计->第二天->宽字符和C语言

2012-10-07 20:55  wid  阅读(5757)  评论(0编辑  收藏  举报

一、回顾C语言中的char数据类型

1>. 在C语言中, 首先我们来声明一个字符型变量:

char c ;

我们也可以在声明时对其进行初始化:

char c = 'A' ;

这时, 字符型变量c就会被用值0x41进行初始化, 0x41也就是ASCII码中的'A'字符;

 

 

2>. 我们还可以定义一个指向一个字符型数据的指针, 例如:

char *p ;

同样我们再让指针p初始化时指向一个字符串:

char *p = "Hello,world!" ;

 

 

3>. 我们再声明一个字符数组:

char a[10] ;

也可以在声明时对其进行初始化:

char a[10] = "Hello" ;

或者:

char a[] = "Hello" ;

  通过对C语言的学习我们可以知道, char型变量为1个字节, 因此, 在char c = 'A' ;中, 变量c的大小即为1字节; 在32位的操作系统中, 一个指针型的变量需要4字节的存储空间, 因此在第二个示例中, char *p = "Hello,world!" ;所需的存储空间为: 4字节指针变量所需的空间 + 字符串"Hello,world!"的12个字节另外再加上一个字节用来表示字符串结束的0。

 

  对于一个字符数组char a[10] ;编译器则会自动保留10个字节的储存空间, 对于char a[] = "Hello" ;这种声明方式, 编译器会根据"Hello"字符串的长度( 5个字符 + 一个结尾0 )来决定初始化时的数组大小。

 

 

 

 

二、宽字符

  通过上午学习的Unicode编码方式中可以知道, 一个Unicode字符占用2个字节的储存空间, 如果我们想用C语言中原有的数据类型表示Unicode的2字节编码类型, char是不行的, char的储存空间为一个字节, 在32位的环境下, int 占4个字节, 而我们仅仅需要的是2字节, 并且是无符号型数据, 因此, 我们可以使用unsigned short int型数据表示一个2字节的字符, unsigned short int为2字节, 正好符合2字节的要求, 当然, 我们也可以将unsigned short int简写为unsigned short。

 

在C语言中的宽字符正是基于short型数据的, 这一数据类型在头文件WCHAR.H中的定义为:

typedef unsigned short wchar_t ;

所以C语言中的宽字符wchar_t数据类型与一个无符号短整形unsigned short一样, 都是16位宽。

如果我们用宽字符wchar_t数据类型定义一个变量并且初始化, 如下:

wchar_t c = 'A' ;

  那么宽字符wchar_t变量c的值为0x0041, 学过汇编的朋友应该知道, 如果使用16位的CPU储存一个字, 将使用两个存储单元, 在这两个存储单元中, 低位字节放在低地址单元中, 高位字节则放在高地址单元中, 所以, 在这里, 处理器依然将从低位内存单元即低位字节开始处理字符, 'A'在内存中的顺序即为 0x41, 0x00。

 

当我们想使用宽字符表示一个字符串, 我们还要通知编译器这个字符串将使用宽字符存储, 我们用大写的字母'L'(表示长整形)来将这一消息告诉编译器, 例如:

wchar_t *p = L"Hello" ;

那么这个字符串"Hello"将会使用12个字节来储存, 这12个字节存储单元的内容为:"Hello"这5个字符占10个字节, 另外加上表示结束的0占两个字节。

 

 

 

 

三、有关宽字符的函数

在C语言的学习中, 字符串处理函数我们经常使用, 比如:

unsigned int strlen(char *s); //求字符串的长度

char *strcat( char *dest, char *src )  ; //将src所指字符串连接到dest结尾处

int strcmp( char *s1, char * s2 ) ;  //将字符串s1与s2比较

char *strcpy( char *dest, char *src ) ;  //将src所指字符串覆盖方式复制到dest中

...

这些函数极大的方便了我们对字符串的处理, 遗憾的是, 在宽字符类型的字符串中, 这些函数将不再适用, 例如我们使用strlen求一个宽字符字符串的长度, 代码如下:

#include <stdio.h>
#include <string.h>

int main()
{
  wchar_t *p = L"Hello" ;
  printf( "%d\n", strlen(p) ) ;

  return 0 ; }

运行后显示的结果为1, 很显然, 它没有求出正确的长度, 这是因为, 宽字符字符串"Hello"在一段内存中存储的值如下:

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

这是因为当strlen找到该字符串的第一个0时就认为该字符串已经结束了, 所以得到的长度为1, strlen统计到的这一个字符即为0x48表示的'H'。

  幸运的是, 虽然这些字符串处理函数不支持对宽字符的处理, 但是我们可以使用为宽字符处理准备的函数, C语言中每个字符串处理函数对应的都有其宽字符版本的字符串处理函数, 这些函数定义在STRING.H头文件和WCHAR.H中, 例如strlen函数响应的宽字符版本为wcslen, wcslen函数在STRING.H的声明如下:

size_t wcslen(const wchar_t *);

size_t为无符号整形unsigned int的别名, STRING.H在头文件的定义如下:

typedef unsigned int size_t;

我们尝试使用宽字符处理函数wcslen()求宽字符字符串的长度:

#include <stdio.h>
#include <string.h>

int main()
{
  wchar_t *p = L"Hello" ;
  printf( "%d\n", wcslen( p ) ) ;

  return 0 ; }

编译运行后的结果显示为5, 是正确的, 同样, 在宽字符中常用的字符串处理函数如下:

函数名

函数原型

函数功能

返回值

wcscat

wchar_t *wcscat(wchar_t *s1, const wchar_t *s2);

将s2所指的字符串连接到s1后面

s1所指字符串的首地址

wcschr

wchar_t *wcschr(const wchar_t *s, wchar_t c);

在s字符串中找到c字符第一次出现的位置

若找到, 则返回该字符的地址, 否则返回NULL

wcscmp

int wcscmp(const wchar_t *s1, const wchar_t *s2);

让字符串s1与字符串s2进行比较

s1 < s2, 返回负数; s1 == s2, 返回0;s1 > s2, 返回正数

wcscpy

wchar_t *wcscpy(wchar_t *s1, const wchar_t *s2);

将s2所指字符串覆盖方式复制到s1中

s1所指的字符串的首地址

wcslen

size_t wcslen(const wchar_t *s);

求s所指字符串的长度

返回有效字符的个数

wcsstr

wchar_t * wcsstr(const wchar_t *s1, const wchar_t *s2);

找出字符串s2在字符串s1中第一次出现的位置

若找到, 则返回该位置的地址, 否则返回NULL

 

 

四、遗留问题:关于TEXT()

昨天在学习过程中有个遗留问题:

在MessageBox对话框中, MessageBox( NULL, TEXT("Hello,world!"), TEXT("MessageBox"), 0 );使用TEXT()的作用是什么呢?

在今天的学习中终于找到了答案。

 

  在定义wchar_t宽字符类型的字符串时, 我们需要在字符串前面加上个大写字母'L'来告诉编译器这是一个wchar_t宽字符型的字符串, 而TEXT()正是为了解决在定义如何不加前面的'L'的。

 

当我们使用宽字符时, 打开TCHAR.H这个头文件向下查找, 会看到:

#define __T(x)      L ## x

这句宏定义, 在ANSI C标准的预处理中, 符号"##"被解释为"令牌粘贴", 作用是使得字母'L'与宏参数连接在一起, 假如参数x为"Hello", 那么经过L ## x处理后"Hello"就变成了L"Hello"。

这看起来和TEXT()没什么关系, 继续向下查找'__T(x)'会看到:

#define _T(x)       __T(x)

#define _TEXT(x)    __T(x)

这两行宏定义, 到这里仍然没有看到TEXT(), 有了这个思路, 笔者就尝试着找到TEXT()宏, 经过搜索引擎的帮助, 终于找到TEXT宏是定义在WINNT.H头文件中, 找到了相关的定义如下:

#define __TEXT(quote) L##quote      // r_winnt

//与

#define TEXT(quote) __TEXT(quote)   // r_winnt

当使用wchar_t类型的宽字符时, 使用"令牌粘贴", 这样, 使用TEXT宏时就避免了在字符串前面加上一个大写字母'L'了。

 

--------------------

 

Wid, 2012.10.07