书籍:《Visual C++ 2017从入门到精通》的2.7 字符串
环境:visual studio 2022
内容:几个字符串类型->(将单字节char*转换为宽字节wchar_t *)
**StringCchPrintf()
详解**
StringCchPrintf()
是 Windows API 中 StrSafe 系列的安全字符串操作函数之一,用于格式化字符串并安全地将结果写入目标缓冲区。其核心优势在于 防止缓冲区溢出,通过严格限制写入的字符数,避免传统函数(如 sprintf
)的潜在安全漏洞。
函数原型
HRESULT StringCchPrintf(
_Out_ LPSTR pszDest, // 目标缓冲区(ANSI)
_In_ size_t cchDest, // 目标缓冲区大小(字符数,含 '\0')
_In_ LPCSTR pszFormat, // 格式化字符串(ANSI)
... // 可变参数
);
Unicode 版本
HRESULT StringCchPrintfW(
_Out_ LPWSTR pszDest,
_In_ size_t cchDest,
_In_ LPCWSTR pszFormat,
...
);
参数说明
参数 | 类型 | 描述 |
---|---|---|
pszDest | LPSTR /LPWSTR | 目标缓冲区指针,用于存储格式化后的字符串。 |
cchDest | size_t | 目标缓冲区的最大容量(字符数,包含结尾的 \0 )。 |
pszFormat | LPCSTR /LPCWSTR | 格式化字符串(如 "Hello, %s!" ),支持与 printf 相同的格式说明符。 |
... | 可变参数 | 与 pszFormat 中占位符对应的参数。 |
返回值
- 成功:返回
S_OK
(0)。 - 失败:返回错误码(如
STRSAFE_E_INSUFFICIENT_BUFFER
表示缓冲区不足)。
核心功能
-
安全格式化:
- 自动截断超出缓冲区容量的内容,确保不会溢出。
- 严格限制写入的字符数(不超过
cchDest - 1
,留出空间给\0
)。
-
错误处理:
- 通过返回值明确指示错误类型,避免未定义行为。
使用示例
1. 基础用法(ANSI)
#include <strsafe.h>
void TestStringCchPrintf() {
char buffer[20];
HRESULT hr = StringCchPrintf(buffer, ARRAYSIZE(buffer), "Hello, %s!", "World");
if (SUCCEEDED(hr)) {
printf("Result: %s
", buffer); // 输出:Hello, World!
} else {
printf("Error: 0x%08X
", hr);
}
}
2. Unicode 用法
#include <strsafe.h>
void TestStringCchPrintfW() {
WCHAR buffer[20];
HRESULT hr = StringCchPrintfW(buffer, ARRAYSIZE(buffer), L"Hello, %s!", L"World");
if (SUCCEEDED(hr)) {
wprintf(L"Result: %s
", buffer); // 输出:Hello, World!
}
}
3. 缓冲区不足时的处理
char buffer[5];
HRESULT hr = StringCchPrintf(buffer, ARRAYSIZE(buffer), "This is a long string");
if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) {
printf("缓冲区不足,需要更大的空间!
");
}
与 sprintf_s
的对比
特性 | StringCchPrintf() | sprintf_s |
---|---|---|
安全性 | 更严格,自动截断溢出内容。 | 需手动指定缓冲区大小,否则可能溢出。 |
返回值 | 返回 HRESULT ,明确错误类型。 | 返回写入的字符数,需手动检查溢出。 |
代码页支持 | 支持 ANSI 和 Unicode 版本。 | 仅支持特定代码页(需手动转换)。 |
注意事项
-
缓冲区大小计算:
- 始终使用
ARRAYSIZE(buffer)
或显式指定缓冲区容量,避免硬编码。 - 示例:
char buffer[256]; StringCchPrintf(buffer, sizeof(buffer), ...); // 错误!应使用 ARRAYSIZE(buffer) StringCchPrintf(buffer, ARRAYSIZE(buffer), ...); // 正确
- 始终使用
-
格式说明符:
- 支持与
printf
相同的格式说明符(如%d
,%s
,%x
)。 - 避免使用宽字符格式说明符(如
%ls
)与非宽字符缓冲区混用。
- 支持与
-
错误处理:
- 必须检查返回值,处理可能的错误(如缓冲区不足)。
- 示例:
if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { // 动态扩展缓冲区并重试 }
高级用法
1. 动态扩展缓冲区
void SafeFormatDynamic() {
size_t initialSize = 64;
char* buffer = (char*)malloc(initialSize);
if (!buffer) return;
HRESULT hr = StringCchPrintf(buffer, initialSize, "Initial: %s", "Hello");
if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) {
free(buffer);
initialSize *= 2;
buffer = (char*)malloc(initialSize);
// 重试...
}
}
2. 追加字符串
void AppendString() {
char buffer[100] = "Hello";
size_t remaining = ARRAYSIZE(buffer) - strlen(buffer) - 1;
StringCchPrintf(buffer + strlen(buffer), remaining, ", %s!", "World");
}
总结
场景 | 解决方案 | 示例代码 |
---|---|---|
基础格式化 | 直接调用 StringCchPrintf | StringCchPrintf(buffer, size, "Hello %s", "World"); |
缓冲区不足 | 检查 STRSAFE_E_INSUFFICIENT_BUFFER 并扩展缓冲区 | 动态分配内存后重试 |
Unicode 支持 | 使用 StringCchPrintfW | StringCchPrintfW(buffer, size, L"%s", L"世界"); |
通过合理使用 StringCchPrintf()
,可以有效避免传统字符串操作的漏洞,提升代码的安全性和健壮性。