第一部分,必备知识

第2章 字符和字符串处理

概述: Windows支持ANSI和Unicode两种字符集,自Windows NT起内核全部采用Unicode实现。编写Windows程序时应使用Unicode以支持多语言和国际化,并通过通用类型和宏实现代码兼容性。

字符编码:ANSI与Unicode的区别

//ANSI字符:8位,最多表示256种字符,无法表示中文、日文等多语言字符
char c = 'A'; //ANSI字符类型

//Unicode字符:16位(UTF-16),Windows使用wchar_t表示,可表示65536种字符
wchar_t w = L'A'; //Unicode字符,前缀L表示宽字符

//Windows NT内核全部采用Unicode实现,ANSI版本函数只是转换为Unicode后调用
//因此使用Unicode函数直接调用内核,性能更好;ANSI函数需要额外转换开销
//Windows头文件通过宏定义支持ANSI和Unicode的通用编程

#ifdef UNICODE
typedef wchar_t TCHAR; //Unicode版本
typedef wchar_t* PTSTR;
typedef const wchar_t* PCTSTR;
#define TEXT(quote) L##quote //自动加L前缀
#else
typedef char TCHAR; //ANSI版本
typedef char* PTSTR;
typedef const char* PCTSTR;
#define TEXT(quote) quote //不加前缀
#endif

//使用示例:编写可同时编译为ANSI或Unicode的代码
TCHAR szBuffer[100]; //根据项目设置自动选择char或wchar_t
PTSTR pszString = szBuffer; //指向TCHAR的指针
PCTSTR pszConst = TEXT("Hello"); //自动添加L前缀或保持原样

//Windows函数也提供宏自动选择A或W版本
#ifdef UNICODE
#define CreateFile CreateFileW
#define MessageBox MessageBoxW
#else
#define CreateFile CreateFileA
#define MessageBox MessageBoxA
#endif

Windows通用字符类型与宏

为了编写可同时支持ANSI和Unicode的代码,Windows头文件定义了一系列通用类型和宏。通过定义或取消定义UNICODE宏,可以控制TCHAR等类型映射到char还是wchar_t。TEXT宏会根据UNICODE定义自动添加L前缀。Windows API函数也提供了类似的宏机制,如CreateFile在Unicode下实际调用CreateFileW,在ANSI下调用CreateFileA。

//Windows头文件通过宏定义支持ANSI和Unicode的通用编程

#ifdef UNICODE
typedef wchar_t TCHAR; //Unicode版本
typedef wchar_t* PTSTR;
typedef const wchar_t* PCTSTR;
#define TEXT(quote) L##quote //自动加L前缀
#else
typedef char TCHAR; //ANSI版本
typedef char* PTSTR;
typedef const char* PCTSTR;
#define TEXT(quote) quote //不加前缀
#endif

//使用示例:编写可同时编译为ANSI或Unicode的代码
TCHAR szBuffer[100]; //根据项目设置自动选择char或wchar_t
PTSTR pszString = szBuffer; //指向TCHAR的指针
PCTSTR pszConst = TEXT("Hello"); //自动添加L前缀或保持原样

//Windows函数也提供宏自动选择A或W版本
#ifdef UNICODE
#define CreateFile CreateFileW
#define MessageBox MessageBoxW
#else
#define CreateFile CreateFileA
#define MessageBox MessageBoxA
#endif

C运行库中的安全字符串函数

传统的C字符串函数(如strcpy、strcat)不检查目标缓冲区大小,容易导致缓冲区溢出漏洞。C运行库提供了带_s后缀的安全版本,要求显式指定目标缓冲区大小。Windows还提供了StringCch和StringCb系列函数,它们返回HRESULT以便进行错误处理,并且明确区分字符计数(cch)和字节计数(cb)。这些函数在缓冲区不足时会安全地截断字符串,而不是造成溢出。

//传统C字符串函数不安全,容易导致缓冲区溢出,应使用安全版本

//不安全:strcpy, strcat, sprintf等不检查目标缓冲区大小
char dest[10];
strcpy(dest, "This is a very long string"); //缓冲区溢出!崩溃或安全漏洞

//安全版本:strcpy_s, strcat_s, sprintf_s等带_s后缀的函数
errno_t strcpy_s(char* dest, size_t destSize, const char* src);
errno_t strcat_s(char* dest, size_t destSize, const char* src);
int sprintf_s(char* buffer, size_t sizeOfBuffer, const char* format, ...);

//使用示例
char dest[10];
strcpy_s(dest, _countof(dest), "short"); //安全,目标大小明确

//Windows特有的安全函数:StringCch系列(推荐)
//ch = character count,以字符数计算大小
HRESULT StringCchCopy(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc);
HRESULT StringCchCat(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc);
HRESULT StringCchPrintf(LPTSTR pszDest, size_t cchDest, LPCTSTR pszFormat, ...);

//StringCb系列:以字节数计算大小
HRESULT StringCbCopy(LPTSTR pszDest, size_t cbDest, LPCTSTR pszSrc);

//使用示例
TCHAR szPath[MAX_PATH];
StringCchCopy(szPath, _countof(szPath), TEXT("C:\\Windows\\System32"));
StringCchCat(szPath, _countof(szPath), TEXT("\\kernel32.dll"));

ANSI与Unicode字符串的转换

在实际编程中,有时需要在ANSI和Unicode之间进行转换,例如与遗留系统交互或处理特定文件格式。Windows提供了MultiByteToWideChar和WideCharToMultiByte两个函数进行转换。MultiByteToWideChar将多字节字符(如ANSI)转换为宽字符(Unicode),WideCharToMultiByte则相反。这两个函数都支持指定代码页(如CP_ACP表示当前ANSI代码页),并且可以处理无法转换的字符。

//有时需要在ANSI和Unicode之间转换,使用MultiByteToWideChar和WideCharToMultiByte

//ANSI转Unicode
int MultiByteToWideChar(
UINT CodePage, //代码页,常用CP_ACP(ANSI)或CP_OEMCP
DWORD dwFlags, //标志,通常0
LPCSTR lpMultiByteStr, //源ANSI字符串
int cbMultiByte, //源字符串字节数,-1表示自动计算(含终止符)
LPWSTR lpWideCharStr, //目标Unicode缓冲区
int cchWideChar //目标缓冲区字符数
);

//Unicode转ANSI
int WideCharToMultiByte(
UINT CodePage, //代码页
DWORD dwFlags, //标志
LPCWSTR lpWideCharStr, //源Unicode字符串
int cchWideChar, //源字符串字符数,-1表示自动计算
LPSTR lpMultiByteStr, //目标ANSI缓冲区
int cbMultiByte, //目标缓冲区字节数
LPCSTR lpDefaultChar, //无法转换字符的替代字符
LPBOOL lpUsedDefaultChar //是否使用了替代字符
);

//使用示例:ANSI转Unicode
char szANSI[] = "Hello, 世界";
wchar_t szUnicode[100];
int nLen = MultiByteToWideChar(CP_ACP, 0, szANSI, -1, szUnicode, 100);

总结:

编写Windows程序应始终使用Unicode字符集以支持国际化和直接调用NT内核。使用通用类型(TCHAR, PTSTR, PCTSTR)和TEXT宏编写可同时支持ANSI和Unicode的代码。避免使用传统C字符串函数,改用安全版本(_s后缀)或Windows特有的StringCch/StringCb系列函数。始终显式指定缓冲区大小,使用_countof宏计算元素个数。进行ANSI和Unicode转换时使用MultiByteToWideChar和WideCharToMultiByte函数。