在C++编程中,可能遇到的字符串的种类实在是太多了,只有清楚每一种的实现原理,并熟记一些转换函数就能够在实际开发中灵活应用。
常见的字符串类型:
1、 基本类型,C风格:char*,const char*,wchar_t,const wchar_t*(在早期的VS中可能以unsignedshort表示UNICODE),以及windows中常用的宏定义CHAR*,TCHAR*,WCHAR*,LPSTR,LPTSTR,LPWSTR,LPCSTR,LPCTSTR,LPCWSTR。
2、 字符串封装类,C++风格:CString(根据工程设置自动转换为CStringA或CStringW),CAtlString(在ATL工程中的CString形式,也分CAtlStringA和CAtlStringW)。
3、 COM平台下的字符串表示:BSTR,CComBSTR,_bstr_t。
4、 跨平台标准C++字符串stl::string/wstring。
个人觉得,虽然类型这么多,但是其实只需要掌握三种基本的类型一切都简单了。三种基本类型就是LPSTR,LPWSTR和BSTR。
先说LPSTR,它是char* 的宏定义,最普的一种类型。注意,它并不一定是ASCII的,在微软的windows系统中,它可能是GBK的,也可能是UTF-8的。
再说LPWSTR,它是wchar_t*的宏定义,也是很普通的类型,它一定是UNICODE的。
最后是BSTR,它相对复杂一点。BSTR是UNICODE的,但是它却和LPWSTR完全一样。
BSTR和LPWSTR的区别:BSTR的字符串首字符的前面,还有四个字节,它是用来描述字符串长度的。
LPWSTR字符串的内存形式(以L”123”为例)
1 | 0 | 2 | 0 | 3 | 0 | 0 | 0 | N/A | N/A |
一个字符占两个字节,但因为是ASCII字符,所以第2个字节为0。最后有2个字节组成一个wchar_t形式的 ‘\0’表示结束,LPWSTR变量里存的是1所在的字节的地址。
BSTR字符串的内存形式(以L”123”为例)
3 | 0 | 0 | 0 | 1 | 0 | 2 | 0 | 3 | 0 | 0 | 0 | N/A | N/A | N/A | N/A |
同样,一个字符占两个字节,每个字符的第二个字节为0,最后有零结束标志,BSTR变量里存的是1所在字节的地址。但是实际上BSTR使用的内存段还包括1前面的四个字节。这四个字节所代表的数字刚好是字符串的长度。
代码验证上面结论:
调试运行时查看buf1和buf2的结果:
LPWSTR与LPSTR之间的转换,如果不涉及编码问题,数据来源和使用都是在本系统中,那一般用W2A,A2W。如果使用了TCHAR,那就用T2A,A2T,T2W,W2T等宏进行转换(这种转换都是使用系统默认的编码,一般是GBK)。如果涉及到编码,比如你的系统在和别的系统打交到,可能字符串穿过来是UTF-8编码的多字节字符串,那就要使用MultiByteToWideChar和WideCharToMultiByte这两个函数进行转换了。
LPWSTR和BSTR的转换:
LPWSTR –>BSTR: BSTR pBstr = SysAllocString(LPWSTR);
BSTR->LPWSTR: 从技术上讲,LPWSTR pwstr =(LPWSTR)pBstr 是行得通的,但是这样代码的逻辑独立性就不够强。应该用LPWSTR pwstr =wcscpy((LPWSTR)pBstr);这样保证BSTR和LPWSTR是独立的字符串。要独立的原因是因为BSTR和LPWSTR本身属于不同的体系,释放内存的方式也不一样。前者是SysFreeString,后者用delete。
LPSTR和BSTR的转换:用_com_util::ConvertBSTRToString()和_com_util::ConvertStringToBSTR()
至于那些封装类,比如CString,CComBSTR,就好理解了。它们只是封装了一个LPTSTR和BSTR作为成员变量,并加了一些操作的函数而已。
在使用LPCTSTR的地方,可以直接用CString作为参数,因为它有隐式转换成LPCTSTR的运算符重载。