在VC++编程过程中我们经常会与字符串打交道,比如向API函数传入字符串参数,从API函数接收返回的字符串,为此我们要定义合适的字符串常量或变量,从而又涉及到各种与字符串相关的数据类型,所以要正确地编写涉及字符串的程序,有一些基本概念需要弄清楚。
基本数据类型
我在前面《浅谈Windows字符集》中对Windows中的字符表示做了简单介绍。通常字符在Windows中被表示为一个字节或两个字节。ANSI编码字符用一个字节表示,Unicode字符用两个字节表示。在VC++中分别用char和wchar_t两种基本数据类型表示(但wchar_t和Unicode并不等同,只是Unicode编码以wchar_t类型存储而已)。在VC++中,wchar_t还被定义为WCHAR类型。
如果我们需要处理ANSI类型的字符,就要用到char数据类型,如果要处理Unicode类型的字符,就要用wchar_t类型,那么我们是不是每个函数都要写两个版本以适应不同的字符编码格式呢?答案是不需要,VC++环境提供了通用的方法。
VC++定义了新的通用数据类型TCHAR:
#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
即如果定义了_UNICODE,TCHAR就被解释为wchar_t,否则会被解释为char。这样我们写代码时用TCHAR类型就可以了,编译时会根据项目是否定义了_UNICODE宏来决定将其解释为何种数据类型。_UNICODE宏是通过在项目属性中设置使用的字符集来定义的,见下图。
要使用TCHAR类型,需要在源文件中(直接或间接)包含TCHAR.H。
将字符数据用TCHAR类型表示后,操作函数也要随之改变。比如,要获取字符串长度,不要再用
size_t strlen(const char*);
也不要再用
size_t wcslen(const wchar_t*);
而是改用更通用的原型:
size_t _tcslen(const TCHAR*);
实际上_tcslen不是真正的函数,它是以宏的形式定义的:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
这样对于程序员来说,代码的编写就简便了许多,也不容易出错。
类似地,Windows大多数以字符串作为输入参数的API都是这样定义的,比如SetWindowText()函数,它实际上有两个版本,SetWindowsTextA()接受ANSI格式的字符串,SetWindowTextW()接受Unicode格式的字符串,SetWindowText()实际上被定义为宏:
#ifdef _UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
只有少数函数只有一个版本,比如ReadDirectoryChangesW()就没有ANSI版本。更进一步,那些定义了ANSI和Unicode两个版本的API函数实际只真正实现了Unicode版本,当调用ANSI版本时,其内部将ANSI转换为Unicode格式,再调用Unicode版本函数,所以,编程时只使用Unicode版本会提高效率。
字符串常量的定义
我们通过在字符串两端加双引号来表示字符串常量,但这是ANSI格式的字符串常量,要定义Unicode字符串常量,还需在前面个加“L”前缀:
WCHAR wcStr[]=L"This is a UNICODE string.";
“L”后面所有的字符都用两字节表示,包括末尾的NULL,所以UNICODE字符串的字节长度总是2的倍数。ANSI和UNICODE字符串的字节长度都可以用sizeof(TCHAR)的倍数表示。
VC++也提供了两个宏来确保字符串以正确的格式定义,分别是_T和TEXT。
#ifdef _UNICODE
#define _T(c) L##c
#define TEXT(c) L##c
#else
#define _T(c) c
#define TEXT(c) c
#endif
这两个宏的含义是一样的,TEXT的可读性更好一些。我们可以用这两个宏表示字符串常量:
TCHAR tStr[]=_T("This may be an ANSI or Unicode string.");
TCHAR tstr2[]=TEXT("This may be an ANSI or Unicode string.");
这两个宏也可用于单个字符,如_T('C'),但不可用于转换字符串变量。
字符串指针
现在可以用多种形式表示字符串指针了。在C/C++中,可以用char *, wchar_t*分别表示一字节和两字节字符串的指针,在前面加上const关键字表示常量字符串指针。在VC++中,又定义了WCHAR* ,与wchar_t*等效,TCHAR*根据_UNICODE的定义决定被解释为char*或wchar_t*。
在VC++中,可以用以下更简洁的形式来声明字符串指针:
LPSTR
typedef char* LPSTR;
LPCSTR
typedef const char* LPCSTR;
LPWSTR
t ypedef WCHAR* LPWSTR;
LPCWSTR
typedef const WCHAR* LPCWSTR;
LPTSTR
typedef TCHAR* LPTSTR;
LPCTSTR
typedef cosnt TCHAR* LPCTSTR;
小结
通过以上的简单梳理,我们对编程时该怎么使用字符串有了大致思路,只要我们设置好项目使用的字符串编码,留意一下各API函数的输入输出参数的类型,再有针对性地进行变量的声明,相信是可以避免大部分的字符串数据类型相关的错误的。