使用 wcsncpy_s 复制字符串时缓冲区太小
Posted
技术标签:
【中文标题】使用 wcsncpy_s 复制字符串时缓冲区太小【英文标题】:Buffer too small when copying a string using wcsncpy_s 【发布时间】:2010-07-09 18:02:24 【问题描述】:这个 C++ 代码有点蹩脚,但我需要维护它。我似乎无法弄清楚“缓冲区太小”的问题。我正在使用 Visual Studio 2010。我将根据我在调试器中看到的值提出重现所需的最少代码。抱歉,我不会测试实际的 sn-p 本身。另外,由于我的系统剪贴板在调试时“忙”,我不能只是复制和粘贴,所以一些错误可能会在某处蔓延,但我会仔细检查内容。相信我,你不想看到整个函数 - 它太长了,没有任何意义:)
来自 tchar.h
#define _tcsncpy_s wcsncpy_s
来自 afxstr.h:
typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;
来自 WinNT.h:
typedef WCHAR TCHAR, *PTCHAR;
哦,伙计,这些宏似乎永远不会结束。我会停在这里。 最后,来自 myfile.cpp:
CString str; // Actually a function parameter with value "07/02/2010"
DWORD nIndex = 10;
DWORD nLast = 0;
LPCTSTR psz = (LPCTSTR) str; // Debugger says that it also gets "07/02/2010".
CString s;
_tcsncpy_s(
s.GetBuffer((int) (nIndex - nLast + 1)), // I added the " + 1" part hoping to fix a bug, but that changed nothing
nIndex - nLast,
psz + nLast,
(size_t) (nIndex - nLast)
);
我打了一个断言,调试器打开tcsncpy_s.inl
,最后带有以下代码:
53 ...
54 if (available == 0)
55
56 if (_COUNT == _TRUNCATE)
57
58 _DEST[_SIZE - 1] = 0;
59 _RETURN_TRUNCATE;
60
61 RESET_STRING(_DEST, _SIZE);
=>62 _RETURN_BUFFER_TOO_SMALL(_DEST, _SIZE);
63
64 _FILL_STRING(_DEST, _SIZE, _SIZE - available + 1);
65 _RETURN_NO_ERROR;
66
67
68
调试器指向第 62 行:_RETURN_BUFFER_TOO_SMALL
。不幸的是,我在tcsncpy_s.inl
中无法查看事物的值。也许有经验的编码员可以告诉我这里发生了什么?我相信(也许是错误的)这段代码已经很老了,并且没有考虑到 Unicode。解决这个问题的最佳方法是什么?
【问题讨论】:
【参考方案1】:strncpy_s
的第四个参数是要从源缓冲区复制的字符数,它不考虑终止 null - 即,实际上,如果源缓冲区包含具有 (nIndex - nLast)
或更多字符,然后(nIndex - nLast)
将被复制,然后 also 将附加一个空字符。因此,目标缓冲区必须准备好接受(nIndex - nLast + 1)
字符,以解释该空值。
现在您的 +1 似乎可以做到这一点,但您也应该在 strncpy_s
的第二个参数中反映它,它告诉它缓冲区有多大。将其更改为(nIndex - nLast + 1)
,它应该可以工作。
【讨论】:
酷!我想发表有用的评论,那么我可以链接到的最佳在线资源是什么?【参考方案2】:传递给 wcsncpy_s() 的大小是缓冲区大小,而不是缓冲区可以存储的字符数。它包括零终止符。您需要添加 1。
【讨论】:
谢谢。我认为你和 Pavel 说了同样的话,但他详细阐述了一些。我可以在评论中描述这种智慧的最佳官方来源是什么? 您可以在 msdn.microsoft.com 和 ***.com 之间进行选择。我推荐后者。 大声笑,你的意思是***.com/questions/3215310/…? 是的,那个。该链接的有效时间可能比任何 MSDN 链接都要长。 不确定是否需要链接... 一个简单的注释“+1 用于终止 NULL 字符”就足够了。如果有人好奇,他们总是可以打开 strncpy_s 的文档。【参考方案3】:来自wcsncpy_s()
(http://msdn.microsoft.com/en-us/library/5dae5d43.aspx) 的文档:
这些函数尝试将 strSource 的前 D 个字符复制到 strDest,其中 D 是 count 和 strSource 长度中的较小者。 如果这些 D 字符可以放入 strDest(其大小以 numberOfElements 给出)并且仍然为空终止符留出空间,则复制这些字符并附加一个终止空;否则,strDest[0] 设置为空字符并调用无效参数处理程序
因此,计数必须以字符(元素)而不是字节来指定,您已经在这样做了,并且目标缓冲区大小必须考虑空终止符,您在缓冲区中腾出空间,但不告诉wcsncpy_s()
(此处称为_tcsncpy()
):
_tcsncpy_s(
s.GetBuffer((int) (nIndex - nLast + 1)), // I added the " + 1" part hoping to fix a bug, but that changed nothing
nIndex - nLast + 1,
psz + nLast,
(size_t) (nIndex - nLast)
);
【讨论】:
以上是关于使用 wcsncpy_s 复制字符串时缓冲区太小的主要内容,如果未能解决你的问题,请参考以下文章
Oracle 数据访问 ORA-06512: 字符串缓冲区太小