Windows 開發中最讓人頭疼的莫過於琳瑯滿目的字串類型:char*、wchar_t*、LPSTR、LPTSTR、BSTR、CString、std::string……本文從字符本質出發,系統梳理類型體系、命名規則、相互轉換最佳實踐。
char,1 字節/字符,編碼取決於當前代碼頁(CP_ACP)。wchar_t,2 字節/字符,固定寬度。Windows 內核原生使用 Unicode,ANSI API 實際上是內部轉換後調用 Unicode 版本,效率較低。
| 基礎類型 | 指針類型 | 常量指針 | 含義 |
|---|---|---|---|
CHAR | PSTR/LPSTR | PCSTR/LPCSTR | ANSI 字符/字串 |
WCHAR | PWSTR/LPWSTR | PCWSTR/LPCWSTR | Unicode 字符/字串 |
TCHAR | PTSTR/LPTSTR | PCTSTR/LPCTSTR | 視 UNICODE 宏切換 ANSI/Unicode |
L 前綴(Long Pointer)在 32/64 位系統無實際區別,純歷史遺留。
#ifdef UNICODE
#define __TEXT(q) L##q
typedef WCHAR TCHAR;
#else
#define __TEXT(q) q
typedef CHAR TCHAR;
#endif
#define TEXT(q) __TEXT(q)
// 用法
TEXT("Hello") // UNICODE=on → L"Hello",off → "Hello"
LPCTSTR,MFC 專項首選。CHAR* == PSTR == LPSTR WCHAR* == PWSTR == LPWSTR TCHAR* == PTSTR == LPTSTR
方法一:ATL/MFC 宏(簡單、栈分配,慎用於大循環)
USES_CONVERSION;
WCHAR* wsz = L"Unicode";
CHAR* sz = "ANSI";
printf("%s\n", W2A(wsz)); // Unicode → ANSI
wprintf(L"%s\n", A2W(sz)); // ANSI → Unicode
⚠️ 宏使用 alloca 棧分配,大循環/長字串會棧溢出。
方法二:WinAPI 標準函數(堆分配、可控、高性能)
// ANSI → Unicode int len = MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, nullptr, 0); WCHAR* wsz = new WCHAR[len]; MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, wsz, len); // Unicode → ANSI len = WideCharToMultiByte(CP_ACP, 0, wszUnicode, -1, nullptr, 0, nullptr, nullptr); CHAR* sz = new CHAR[len]; WideCharToMultiByte(CP_ACP, 0, wszUnicode, -1, sz, len, nullptr, nullptr);
參數詳解:CP_ACP 當前系統代碼頁,-1 表示以 \0 結尾自動計算長度。
// ANSI → BSTR
BSTR bstr = _bstr_t("ANSI string"); // _bstr_t 管理引用計數
// BSTR → ANSI
_bstr_t wrapper(bstr);
CHAR* sz = wrapper; // 隱式轉換操作符
CString cstr = _T("Hello");
std::string s1 = CT2A(cstr); // MFC/ATL 宏
std::wstring s2 = CT2W(cstr);
// 反向
CString cstr2 = CA2T(stdStr.c_str());
CString cstr3 = CW2T(stdWStr.c_str());
UNICODE/_UNICODE,所有內部 API 用 WCHAR/std::wstring,邊界處轉換。std::string/std::wstring 配合 MultiByteToWideChar/WideCharToMultiByte,避免宏陷阱。_bstr_t/CComBSTR 管理生命週期。掌握類型本質與轉換邊界,是 Windows 系統級開發的基本功。