2

我试图了解 wcsncpy_s 函数的工作原理以及它如何防止缓冲区溢出。首先,根据 MSDN,此函数的参数含义如下:

strDest= 目标字符串。

numberOfElements= 目标字符串的大小。

strSource= 源字符串。

count= 要复制的字符数,或 _TRUNCATE。

现在考虑这段代码:

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

printf("%d\r\n", sizeof(a));//10
printf("%d\r\n", wcslen(a));//9
wprintf(L"%s", a);//ABCDEFGHI

如果我理解这一切,“a”应该最多包含 4 个宽字符加上一个空终止符,现在包含 9 个宽字符。

现在,由于调试断言失败(VS 2005 编译器),以下代码将导致我的应用程序突然终止:

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10);

printf("%d\r\n", sizeof(a));
printf("%d\r\n", wcslen(a));
wprintf(L"%s", a);

有人可以解释一下上面的代码以及 wcsncpy_s 应该如何防止缓冲区溢出吗?

4

3 回答 3

8
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

你在对函数撒谎。你告诉它“a有足够的空间来存储 10 个字符”,而实际上它只有足够的空间来存储 5 个字符。该函数相信您正在向它提供有效信息(它怎么会知道您不是?)

请注意,虽然第二个代码片段出现运行时错误,但第一个代码片段同样错误。两者都写到数组的末尾a

也就是说:您使用了错误的重载wcsncpy_s: 在编译 C++ 代码时,还有一个额外的wcsncpy_s重载,它是一个推断目标数组大小的模板。如果您要将呼叫更改为:

wcsncpy_s(a, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

模板将推断出数组有五个元素并自动将其用作大小。这仅在目标是数组时有效;如果目标是指向数组中初始元素的指针,则它不起作用。

理想情况下,如果您使用 C++,最好完全避免 C 字符串操作:使用std::wstring 或其他一些字符串类型。如果您确实想使用这些处理 C 字符串的函数,至少使用std::vector<wchar_t>std::array<wchar_t, N>代替原始数组:搞砸代码要困难得多。例如,

std::array<wchar_t, 5> a;
wcsncpy_s(a.data(), a.size(), L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

的代码std::vector<wchar_t>将是相同的。请注意,获取指向底层数组的指针和获取该数组的大小遵循相同的形式,因此编写代码很容易,也很容易检查代码是否正确(只需对调用进行简单的视觉检查)。

于 2012-09-28T17:18:30.793 回答
4

在这一行:

wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);

您是在告诉函数 中的10字符有空间a,而实际上只有 . 的空间5

于 2012-09-28T17:17:28.817 回答
2

wcsncpy_s 的定义是

errno_t wcsncpy_s(
wchar_t *strDest,
size_t numberOfElements,
const wchar_t *strSource,
size_t count 
);

strDest 也是您写作的缓冲区。

numberOfElements 是 strDest 缓冲区中有多少元素。

strSource 是您正在读取的缓冲区

count 是 strSource 中要复制到 strDest 的元素数

所以在

wchar_t a[5];
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10); //ERROR

printf("%d\r\n", sizeof(a));
printf("%d\r\n", wcslen(a));
wprintf(L"%s", a);

wchar_t 是 2 个字符宽。

问题是该函数最后会自动为您写入一个空终止符。这会导致溢出,因为它试图在末尾写一个。

wcsncpy_s 没有帮助您解决此错误,因为您告诉它 a 中有 10 个可用元素。实际上只有 5 个元素可用。

于 2012-09-28T17:24:11.967 回答