一、将要讨论的问题
char
wchar_t
TCHAR
_TCHAR
_T
_TEXT
__T
LPSTR
LPCSTR
LPCTSTR
LPWSTR
CString
二、一切的基础
首先,char就不说了,单个字节表示,ansi的方式
wchar_t是Unicode字符的数据类型,宽字符,用两个字节表示,它实际定义在<string.h>里:
typedef unsigned short wchar_t;
我们都知道,在一个工程的属性中有一个选项:
Character set-----àUse UnicodeCharacter set or Use Multi-Byte Character set
它让你在以UNNICODE或MBCS的方式来使用字节
如果选了Use Unicode Character set,也就是说定义了宏:
#define UNICODE
如果Use Multi-Byte Character set,也就是没有定义宏:
#define UNICODE,而定义了宏_MBCS
这是有很大区别的,直接造成了使用字符处理函数等多方面的差异。
尤其是在界面编程时,如果想考虑可移植性(即可在ansi下用,由可在Unicode下用),那么方法一:写两套代码,全部用宏的方式注释开,然后使用预处理,决定是使用Unicode还是MBCS。
方式二:和方式一一样,只不过编译器替我们做了这些,这就是TCHAR、TEXT、_T等一些列宏。
三、TCHAR及ANSI、UNICODE兼容性
TCHAR是定义在其中的一个宏,它视你是否定义了_UNICODE宏而定义成char或者wchar_t。
#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
如果你使用了TCHAR,那么就不应该使用ANSI的strXXX函数或者Unicode的wcsXXX函数了,而必须使用TChar.h中定义的_tcsXXX函数。另外,为了解决刚才提到带“L”的问题,TChar.h中定义了一个宏:“_TEXT”。
以strcpy函数为例子,总结一下:
.如果你想使用ANSI字符串,那么请使用这一套写法:
char szString[100];
strcpy(szString,"test");
.如果你想使用Unicode字符串,那么请使用这一套:
wchar_t szString[100];
wcscpy(szString,L"test");
.如果你想通过定义_UNICODE宏,而编译ANSI或者Unicode字符串代码:
TCHAR szString[100];
_tcscpy(szString,_TEXT("test"));
这样就很清楚了,还要注意一点是_TEXT这个宏
源码是这样的:
#define TEXT(quote) __TEXT(quote) // r_winnt
如果定义了UNICODE,那么,#define __TEXT(quote) L##quote
如果没有定义UNICODE,那么,#define __TEXT(quote) quote // r_winnt
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
如果定义了UNICODE,那么#define __T(x) L ## x
如果没有定义UNICODE,那么#define __T(x) x
很清楚了吧,本质上,_T TEXT _TEXT 这三个宏都是 __T 或者 __TEXT, 而后二者视情况决定在x前是否加上宏L
宏 L 表示将ANSI字符串转换成unicode的字符串,就是每个字符占用两个字节。
综上所述:当既想在ANSI上用,又想在UNICODE上用时,应该使用TCHAR来定义,然后使用_tcsXXX的函数,将字符串常量用TEXT等宏预处理。
四、常见基本字符类型及他们的引申宏定义在MBCS和UNICODE的指向:
Type |
MBCS |
Unicode |
注释 |
TCHAR |
char |
wchar_t |
映射宏,当定义UNICODE时,TCHAR映射到 wchar_t,否则,那么它映射到 char |
_T _TEXT TEXT __T __TEXT |
char |
wchar_t |
ASCII模式下,它们被忽略,也就是说被预处理器删除掉,但是如果定义了UNICODE, 则它们会将常量字符串转换成等价的 UNICODE |
LPTSTR |
TCHAR* (实质上char*) |
TCHAR* (实质上wchar_t*) |
|
LPCTSTR |
const TCHAR* (实质上const char*) |
const TCHAR* (实质上const wchar_t*) |
|
WCHAR |
wchar_t |
wchar_t |
始终是wchar_t |
CHAR |
char |
char |
始终是char |
LPSTR |
char* |
char* |
始终是char* |
LPCSTR |
const char* |
const char* |
始终是const char* |
LPWSTR |
wchar_t* |
wchar_t* |
始终是wchar_t* |
LPCWSTR |
const wchar_t* |
const wchar_t* |
始终是const wchar_t* |
|
|
|
|
C表示const
W表示宽字符
T表示TCHAR,可移植
LP 指针
五、CString
MFC中封装了CString类,让很多初用MFC的人暗爽….但是久了会发现,CString和上述的多种类型转换、或者上述类型之间相互转换起来确实不方便,容易搞晕。
CString是封装了TCHAR,因此将编译条件从MBCS改为UNICODE(或者反过来时)都不需要修改代码,这就是TCHAR这个宏定义当初被引入的初衷。
构造CString对象的时候,会使用TCHAR数据类型,也就是说内部是根据编译条件(MBCS或UNICODE)将初始化数据转换为char或wchar_t的。
很多喜欢在MFC下编程的人一开始很喜欢CString,因为微软把他的基础功能封装的挺不错,不用去管理内存和使用指针。但是它还是有很多问题的,比如滥用operator操作使得内存拷贝越界,不过他的CopyBeforeWrite技术还是不错的,另外,他和stl的vector一样,使用了动态增加空间的方法。具体内部剖析:
http://blog.csdn.net/zp373860147/article/details/6968853
六、一些转换
话说现在,大部分程序开发会是在Unicode编译条件下进行吧。
那么char*和宽字符的互相转换就比较常用了。
1、 Unicode下CString转为char*:
方法一:使用API:WideCharToMultiByte进行转换
CString str = _T("你好world");
//注意:以下n和len的值大小不同,n是按字符计算的,len是按字节计算的
int n = str.GetLength(); // n = 7, len = 9
//获取宽字节字符的大小,大小是按字节计算的
int len = WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),NULL,0,NULL,NULL);
//为多字节字符数组申请空间,数组大小为按字节计算的宽字节字节大小
char * pFileName = new char[len+1]; //以字节为单位
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP,0,str,str.GetLength(),pFileName,len,NULL,NULL);
pFileName[len+1] = '\0'; //多字节字符以'\0'结束
方法二:使用函数:T2A、W2A
(ATL中的函数,使用USES_CONVERSION一定要小心,它们从堆栈上分配内存,直到调用它的函数返回,该内存不会被释放。如果在一个循环中,这个宏被反复调用几万次,将不可避免的产生stackoverflow。在一个函数的循环体中使用A2W等字符转换宏可能引起栈溢出。)
CString str = _T("你好world ");
//声明标识符
USES_CONVERSION;
//调用函数,T2A和W2A均支持ATL和MFC中的字符转换
char * pFileName = T2A(str);
//char * pFileName = W2A(str);//也可实现转换
//注意:有时候可能还需要添加引用#include <afxpriv.h>
2、Unicode下char *转换为CString
方法一:使用API:MultiByteToWideChar进行转换
char * pFileName = "你好world";
//计算char *数组大小,以字节为单位,一个汉字占两个字节
int charLen = strlen(pFileName);
//计算多字节字符的大小,按字符计算。
int len = MultiByteToWideChar(CP_ACP,0,pFileName,charLen,NULL,0);
//为宽字节字符数组申请空间,数组大小为按字节计算的多字节字符大小
TCHAR *buf = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP,0,pFileName,charLen,buf,len);
buf[len] = '\0'; //添加字符串结尾,注意不是len+1
//将TCHAR数组转换为CString
CString pWideChar;
pWideChar.Append(buf);
//删除缓冲区
delete []buf;
方法二:使用函数:A2T、A2W
char * pFileName = "你好world";
USES_CONVERSION;
CString s = A2T(pFileName);
//CString s = A2W(pFileName);
方法三:使用_T宏,将字符串转换为宽字符
//多字节字符集,在vc6和vc7种可以编译通过的语句,但VS2005不能通过,默认为Unicode字符集
//AfxMessageBox("加载数据失败",0);
//书写代码使用TEXT("")或_T(""),文本在UNICODE和非UNICODE程序里都通用
AfxMessageBox(_T("加载数据失败"),0);
//注意:直接转换在基于MBCS的工程可以,但在基于Unicode字符集的工程中直接转换是//不可行的,CString会以Unicode的形式来保存数据,强制类型转换只会返回第一个字符。