我正在尝试了解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应该如何防止缓冲区溢出?
答案 0 :(得分:6)
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>
的代码将完全相同。请注意,获取指向底层数组的指针并获取该数组的大小遵循相同的形式,因此编写代码很容易并且很容易检查代码是否正确(只需要对调用进行简单的目视检查) )。
答案 1 :(得分:3)
在这一行:
wcsncpy_s(a, 10, L"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 9);
你告诉函数10
中有a
个字符的空间,实际上只有5
的空间。
答案 2 :(得分: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是要复制到strDest的strSource中的元素数
所以在
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没有帮助您解决此错误,因为您告诉它有一个可用的10个元素。实际上只有5个元素可用。