如何记忆 C/C++ 中各类字符串类型之间的转换?

char*, const char*, wchar_t*, const wchar_t*, string, CString, LPSTR, LPCTS…
关注者
51
被浏览
11,562

8 个回答

PS:这个问题我答得不太好,随便看看罢。

------

首先你要搞清楚字符串,是一个存储在连续字符数组中的字符的线性的集合。CHAR 和 WCHAR是单个字符的类型,对应了不同字符编码,前者是一个byte,后者是两个byte(编译器决定,或者可以配置,linux gcc中 wchar_t 默认 4 bytes)。前者通常是 ascii,后者通常肯定是 unicode。LPCHAR ,LPWCHAR 就是对应的这个内存的起始地址的指针,也可以说指向字符串的第一个字符,这种字符串叫做 null-terminated string。(当然它仅仅是字符指针也是可以的,如果说它是字符串,那么从这里向高地址望去,第一个值为 0 的字符标记了这个字符串的结束)。const 是暗示给编译器和代码读者的一个修饰语,但是本质上来说,在编译到指令时没有限制。报的运行时错误,是因为常量字符串所在的 segment (section)被标记成只读,不可写。TCHAR 是一个条件编译的东西,被实际映射到 CHAR 或者 WCHAR,取决于你的项目设置。LPCTSTR,也就是 const char* 或者 const wchar_t*,就是提示编译器,后面的代码不能改动这个字符串的内容。string 和 CString,分别是标准库和微软类库提供的对字符串所在内存的类封装。它们自带内存管理功能,目的,通常是为了缓解程序员对内存管理的负担。

举个简单例子,当你看到 my_strcpy (char* p1, const char* p2); 你不需要看其他任何资料,也不需要参数名称的暗示,仅根据参数类型,就能推断出 p1 是 dest,p2 是 src。

当你遇到字符串在不同系统之间交互时(例如程序读取文件,网络传输),你遇到的第一个问题,一定是要双方相互表名自己使用的编码。在 ascii , utf8 和 unicode这些常见编码中转换,你要使用 WideCharToMultiByte 等相关函数。因此,很显然,char* 和 wchar_t * 是不能直接强制类型互转的,除非你明确的知道你正在干什么。而 TCHAR* 就是这两者中的某一个,仅仅在源代码级别时存在,编译后就是实际的东西了。

此外还有 BSTR, _bstr_t,CComBSTR。确实很容易让人晕。

先记最原始的 ANSI 字符串 LPSTR, 被定义成 char *.

LPCSTR 比 LPSTR 多了个 C, 意思是 const, 所以 LPCSTR 是 const char *.

后来 XP 后 微软又把所有 API 增加了 Unicode 版本(实际是重新开发), 于是在 LPSTR 基础上

加了个 W ,即 LPWSTR, 被定义成 wchat_t.

同理 LPCWSTR 被定义成 const wchar_t *.

可是怎么兼容 以前的 ANSI 版本的 API 呢? 微软用 带 T 的宏 来解决的。

如果 定义了Unicode

那么

TCHAR 被定义成 WCHAR (就是 wchar_t)

LPTSTR 被定义成 LPWSTR (wchar_t *)

LPCTSTR 被定义成 LPCWSTR (const wchar_t *)

否则 /* 那就是用了 ANSI 版本了 */

TCHAR 被定义成 CHAR (就是 char 了)

LPTSTR 被定义成 LPSTR (char *)

LPCTSTR 被定义成 LPCSTR (const char *)

对应的,实际的函数名 以 A 结尾的 对应 ANSI 版本,而以 W 结尾的 对应的 Unicode 版本.

(其实 ANSI 版本现在都是 将参数先转成 宽字符,再调用 Unicode 版本)

比如 MessageBoxA 和 MessageBoxW

最后我们用的 MessageBox, 其实也是宏:

如果 定义了Unicode

那么

MessageBox 就是 MessageBoxW

否则

MessageBox 就是 MessageBoxA

在平时的时候,char * 与 const char * 之间的显式转换很少, 即使用到也很容易转.

偶尔麻烦的就是 ANSI 和 Unicode 之间的转换,有俩API:

Unicode- > ANSI: WideCharToMultiByte

ANSI- > Unicode: MultiByteToWideChar

说真的,这俩 API 若是频繁用到,看定义都麻烦. 所以最好自己稍微做封装一下. 以后直接用自己封转后的版本就好了.