面對 TCHAR、std::string、BSTR、_tcs 宏等繁雜字符串類型,本指引總結其由來、用法及相互轉換方法。第一部分介紹三種字符編碼模式,理解它們是掌握字符串類型關系的基石。
所有 string 類基於 C 風格字符串(字符數組)。三種編碼模式對應三種字符類型:
單字節字符含拉丁字母等;雙字節用於東亞/中東語言;Unicode 用於 COM 及 Windows NT 內部。
代碼表示:wchar_t wch = L'1'; wchar_t* wsz = L"Hello";
SBCS "Bob": 42 6F 62 00
Unicode L"Bob": 42 00 6F 00 62 00 00 00 (小端序)
DBCS "日本語": 93 FA 96 7B 8C EA 00 (每字符 2 字節,前導位元組 > 0x7F)
注意:DBCS 中雙字節字符不可視為 WORD 整體,必須按字節序解讀。
str* 族:僅用於 SBCS。wcs* 族:僅用於 Unicode。_mbs* 族:微軟 CRT 擴展,支援 DBCS,兼容 SBCS。若軟體可能在 DBCS 國家(中、日、韓)安裝,應優先用 _mbs* 函數。誤用示例:Unicode 字符串傳給 strlen 會因遇到中間 0 字節而截斷;傳給 wcslen 會越界讀取直到雙零或 GPF。
SBCS/Unicode 可用指針 ++/-- 或下標,因字符定長。DBCS 嚴禁:
反例:檢查路徑尾部反斜槓,用 pLastChar-- 會誤讀後續位元組 0x5C('\' 的值)為單字符,導致邏輯錯誤。正確做法用 CharPrev API 後退一個字符(可能 2 字節)。
// 錯誤
char* pLastChar = strchr(szPath, '\0'); pLastChar--;
if (*pLastChar != '\\') strcat(szPath, "\\");
// 正確
char* pLastChar = _mbschr(szPath, '\0');
pLastChar = CharPrev(szPath, pLastChar); // 後退一個字符
if (*pLastChar != '\\') _mbscat(szPath, "\\");
索引規則:勿用減法得索引(如 &arr[strlen(arr)-1] 同理錯誤)。
所有字符串相關 API/消息均有 A (MBCS) 和 W (Unicode) 兩版本,如 SetWindowTextA/W。頭文件用宏映射:
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
預設 MBCS 建構(未定義 UNICODE)。若改用 Unicode,需刪 _MBCS、加 UNICODE/_UNICODE,但原 char 變量會類型不匹配。
#ifdef UNICODE
typedef wchar_t TCHAR;
#define _T(x) L##x
#else
typedef char TCHAR;
#define _T(x) x
#endif
使用 TCHAR sz[] = _T("text"); 即可同碼碼在 MBCS/Unicode 下編譯。配套通用宏:_tcsrchr (替代 strrchr/_mbsrchr/wcsrchr)、_stprintf、_tfopen 等。完整列表見 MSDN「Generic-Text Routine Mappings」。
| 類型 | MBCS 構建 | Unicode 構建 |
|---|---|---|
| WCHAR | wchar_t | wchar_t |
| LPSTR | char* | char* |
| LPCSTR | const char* | const char* |
| LPWSTR | wchar_t* | wchar_t* |
| LPCWSTR | const wchar_t* | const wchar_t* |
| TCHAR | char | wchar_t |
| LPTSTR | TCHAR* | TCHAR* |
| LPCTSTR | const TCHAR* | const TCHAR* |
SetWindowTheme)。Windows 9x 大多無 Unicode API 實現,故需跑 9x 必用 MBCS。隨 9x 退出歷史舞台,微軟逐漸拋棄 MBCS API。即使當前用 MBCS,也應用 TCHAR 及相關宏,為將來遷移 Unicode 鋪路,同時正確處理 DBCS。