UNICODE:用两个字节表示一个字符的方法。比如字符'A'在ASCII下面是一个字符,可'A'在UNICODE下面是两个字符,高字符用0填充,而且汉字'程'在ASCII下面是两个字节,而在UNICODE下仍旧是两个字节。UNICODE的用处就是定长表示世界文字,据统计,用两个字节可以编码现存的所有文字而没有二义。
MBCS:多字节字符集,是不定长表示世界文字的一种编码。MBCS表示英文字母时就和ASCII一样(这也是我们容易把MBCS和ASCII搞混的原因),但表示其他文字时就需要用多字节。
WINDOWS下面的程序设计可以支持MBCS和UNICODE两种编码的字符串,具体用那种就看程序员定义了MBCS宏还是UNICODE宏。MBCS宏对应的字符串指针是char*(也即LPSTR),UNICODE对应的指针是unsigned short*(也即LPWSTR),为了写程序方便微软定义了类型LPTSTR,在MBCS下他就是char*,在UNICODE下它是unsigned char*,这样你就可以重定义一个宏进行不同字符集的转换了。
1. LPSTR、LPCSTR、LPTSTR、LPCTSTR的意义:
LPSTR:32-bit指针,指向一个字符串,每个字符占1字节,等价于char*
LPCSTR:32-bit指针,指向一个常字符串,每个字符占1字节,等价于const char*
LPTSTR:32-bit指针,指向一个字符串,每字符可能占1字节或2字节,取决于Unicode是否定义,等价于TCHAR *
LPCTSTR:32-bit指针,指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义,等价于const TCHAR *
Windows使用两种字符集ANSI和UNICODE,前者就是通常使用的单字节方式,但这种方式处理像中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符。
Windows NT的所有与字符有关的函数都提供两种方式的版本,而Windows9x只支持ANSI方式。_T一般同字常数相关,如_T("Hello")。如果你编译一个程序为ANSI方式,_T实际不起任何作用。而如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。_T和L的区别在于,不管你是以什么方式编译,L一律用UNICODE方式保存。
① L是表示字符串资源为Unicode的。
例如:wchar_t Str[] = L"Hello World!";//这个就是双子节存储字符
② _T是一个适配的宏,当#ifdef _UNICODE的时候,_T就是L,没有#ifdef _UNICODE的时候,_T就是ANSI的。
例如:LPTSTR lpStr = new TCHAR[32];
TCHAR* szBuf = _T("Hello");
以上两句使得无论是在UNICODE编译条件下还是在MBCS编译条件下都是正确编译的。
MS推荐你使用相匹配的字符串函数,比如处理LPTSTR或者LPCTSTR的时候,不要用strlen ,而是要用_tcslen,否则在UNICODE的编译条件下,strlen不能处理wchar_t*的字符串。
T是非常有意思的一个符号(TCHAR、LPCTSTR、LPTSTR、_T()、_TEXT()...),它表示使用一种中间类型,既不明确表示使用MBCS,也不明确表示使用UNICODE。那到底使用哪种字符集在编译的时候才决定。
char*是指向ANSI字符数组的指针,其中每个字符占据8位(有效数据是除掉最高位的其他7位),这里保持了与传统的C,C++的兼容。LP的含义是长指针(long pointer)。LPSTR是一个指向以‘\0’结尾的ANSI字符数组的指针,与char*可以互换使用,在win32中较多地使用LPSTR。而LPCSTR中增加的‘C’的含义是“CONSTANT”(常量),表明这种数据类型的实例不能被使用它的API函数改变,除此之外,它与LPSTR是等同的。为了满足程序代码国际化的需要,业界推出了Unicode标准,它提供了一种简单和一致的表达字符串的方法,所有字符中的字节都是16位的值,其数量也可以满足差不多世界上所有书面语言字符的编码需求,开发程序时使用Unicode(类型为wchar_t)是一种被鼓励的做法。LPWSTR与LPCWSTR由此产生,它们的含义类似于LPSTR与LPCSTR,只是字符数据是16位的wchar_t而不是char。
然后为了实现两种编码的通用,提出了TCHAR的定义:
如果定义_UNICODE,声明如下:typedef wchar_t TCHAR;
如果没有定义_UNICODE,则声明如下:typedef char TCHAR;
LPTSTR和LPCTSTR中的含义就是每个字符是这样的TCHAR。
CString类中的字符就是被声明为TCHAR类型的,它提供了一个封装好的类供用户方便地使用。
2. LPSTR、LPCSTR、LPTSTR、LPCTSTR之间的转换:
2.1 如何理解LPCTSTR类型?
L表示long,这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32为操作系统中,long、near及far修饰符都是为了兼容的作用。没有实际意义。
P表示这是一个指针。
C表示是一个常量。
T表示在Win32环境中,有一个_T宏,这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。
STR表示这个变量是一个字符串。
LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。同样, LPCSTR就只能是一个ANSI字符串,在程序中我们大部分时间要使用带T的类型定义。LPCTSTR等价于const TCHAR *。
常量字符串ansi和unicode的区分是由宏_T来决定的。但是用_T("abcd")时,字符串"abcd"就会根据编译时是否定义了_UNICODE来决定是char*还是w_char*。同样,TCHAR 也是相同目的字符宏。 看看定义就明白了。简单起见,下面只介绍ansi的情况,unicode可以类推。
2.2 LPCTSTR、CString、LPTSTR的比较
① ansi情况下,LPCTSTR就是const char*,是常量字符串(不能修改的);而LPTSTR就是char*, 即普通字符串(非常量,可修改的)。这两种都是基本类型,而CString是C++类,兼容这两种基本类型是最起码的任务了。
由于const char*最简单(常量,不涉及内存变更,操作迅速),CString直接定义了一个类型转换函数operator LPCTSTR() {......},直接返回它所维护的字符串。当你需要一个const char* 而传入了CString时,C++编译器自动调用 CString重载的操作符LPCTSTR()来进行隐式的类型转换。当需要CString ,而传入了const char* 时(其实 char* 也可以),C++编译器则自动调用CString的构造函数来构造临时的 CString对象。因此CString 和 LPCTSTR基本可以通用。
CString转换到LPCTSTR:
CString cStr;
const char *lpctStr = (LPCTSTR)cStr;
LPCTSTR转换到CString:
LPCTSTR lpctStr;
CString cStr = lpctStr;
② 而LPTSTR等价于char*,意味着程序员随时可能修改里面的数据,这就需要内存管理了(如字符串变长,原来的存贮空间就不够了,则需要重新调整分配内存)。所以不能随便的将const char*强制转换成char*使用,因为这涉及到了安全性问题。
例示:
LPSTR lpstr = (LPSTR)(LPCTSTR)string; //string为CString类对象
首先,先将CString类对象转换为LPCTSTR型变量,然后再强制转换为LPSTR型变量。但是这是一种不安全的使用方法。