2.2 宽字符和C语言
2.2.2 更宽的字符
在C语言中的宽字符正是基于short型数据的, 这一数据类型在头文件WCHAR.H中的定义为:
typedef unsigned short wchar_t ;
所以C语言中的宽字符wchar_t数据类型与一个无符号短整形unsigned short一样, 都是16位宽。
例如:
wchar_t c = 'A' ;
在计算机中保存为0x0041,显示为0x41 0x00
2.2.3 宽字符库函数
在使用strlen计算宽字符长度时:
#include <stdio.h> #include <string.h> int main() { wchar_t *p = L"Hello" ; printf( "%d ", strlen(p) ) ; return 0 ; }
运行后显示的结果为1, 很显然, 它没有求出正确的长度, 这是因为, 宽字符字符串"Hello"在一段内存中存储的值如下:
48 00 65 00 6C 00 6C 00 6F 00 21 00
这是因为当strlen找到该字符串的第一个0时就认为该字符串已经结束了, 所以得到的长度为1, strlen统计到的这一个字符即为0x48表示的'H'。
我们使用wcslen函数计算宽字符长度。同样的还有
函数名 |
函数原型 |
函数功能 |
返回值 |
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 |
2.2.4 维护一个源代码文件
使用TEXT可以避免使用 L"hello"直接使用TEXT("hello")
追溯定义TEXT的地方发现已经加了L
#define TEXT(quote) __TEXT(quote) // r_winnt #define __TEXT(quote) L##quote // r_winnt
因此使用TEXT或者_TEXT都可以不用谢L了
2.3 宽字符和Windows
2.3.1 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宏。
2.3.2 Windows函数调用
MessageBoxA:
WINUSERAPI int WINAPI MessageBoxA (HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) ;
下面是MessageBoxW:
WINUSERAPI int WINAPI MessageBoxW (HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) ;
他们的第2,3个参数都是字符串常量指针
MessageBox会根据参数类型来调用:
#ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif // !UNICODE
2.3.3 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标识符,那么这些函数将接受宽字符串,否则只接受常规字符串。宽字符串版的lstrlenW函数可在Windows 98中执行。
2.3.4 在Windows中使用printf
在Windows程序中不能使用printf函数,而是使用其他的函数:
#include <Windows.h> #include <stdio.h> #include <stdlib.h> int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { char szBuffer[100]; sprintf(szBuffer, "The Sum of %i and %i is %i ", 5, 3, 5 + 3); MessageBox(NULL, szBuffer, TEXT("Hk_Mayfly"), 0x4L | 0x40L); return 0; }
表列出了Microsoft的C执行时期链接库和Windows支持的所有sprintf函数。
ASCII |
宽字符 |
常规 |
|
参数的变数个数 |
|||
标准版 |
sprintf |
swprintf |
_stprintf |
最大长度版 |
_snprintf |
_snwprintf |
_sntprintf |
Windows版 |
wsprintfA |
wsprintfW |
wsprintf |
参数数组的指针 |
|||
标准版 |
vsprintf |
vswprintf |
_vstprintf |
最大长度版 |
_vsnprintf |
_vsnwprintf |
_vsntprintf |
Windows版 |
wvsprintfA |
wvsprintfW |
wvsprintf |
2.3.5 格式化的消息框
#include <windows.h> #include <tchar.h> #include <stdio.h> int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...) { TCHAR szBuffer [1024] ; va_list pArgList ; // The va_start macro (defined in STDARG.H) is usually equivalent to: // pArgList = (char *) &szFormat + sizeof (szFormat) ; va_start (pArgList, szFormat) ; // The last argument to wvsprintf points to the arguments _vsntprintf ( szBuffer, sizeof (szBuffer) / sizeof (TCHAR), szFormat, pArgList) ; // The va_end macro just zeroes out pArgList for no good reason va_end (pArgList) ; return MessageBox (NULL, szBuffer, szCaption, 0) ; } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { int cxScreen, cyScreen ; cxScreen = GetSystemMetrics (SM_CXSCREEN) ; cyScreen = GetSystemMetrics (SM_CYSCREEN) ; MessageBoxPrintf ( TEXT ("ScrnSize"), TEXT ("The screen is %i pixels wide by %i pixels high."), cxScreen, cyScreen) ; return 0 ; }