如何使用 strncpy_s() 函数实现 strncpy() 功能?
Posted
技术标签:
【中文标题】如何使用 strncpy_s() 函数实现 strncpy() 功能?【英文标题】:How to achieve strncpy() functionality with strncpy_s() function? 【发布时间】:2011-02-18 07:18:21 【问题描述】:在某些情况下,我确实需要strncpy()
功能 - 例如,我在预定义接口中有一个函数,该函数传递缓冲区地址和缓冲区大小:
HRESULT someFunction( char* buffer, size_t length );
据记录,我可以在那里复制一个长度不超过 length
的空终止字符串 - 如果它的长度正好是 length
我不会空终止字符串并且调用者知道字符串以空字符或长度 length
结尾,以先发生者为准。
当然我会使用strncpy()
HRESULT someFunction( char* buffer, size_t length )
const char* toCopy = ...
size_t actualLength = strlen( toCopy );
if( actualLength > length )
return E_UNEXPECTED; // doesn't fit, can't do anything reasonable
strncpy( buffer, toCopy, length );
return S_OK;
现在我有了这段代码,需要将它从 Visual C++ 7 迁移到 Visual C++ 9。我编译它并使用 see a warning that strncpy()
is unsafe,我应该改用 strncpy_s()。
strncpy_s()
旨在始终以空值终止缓冲区,因此在上述情况下我不能将其用作直接替换。我必须在比length - 1
长的字符串上返回E_UNEXPECTED
(不是以前的length
),否则一旦字符串为length
或更长,它就会触发无效参数错误处理程序,否则程序将运行到未定义的行为。
到目前为止我应用的解决方案是定义一个_CRT_SECURE_NO_WARNINGS
并让编译器关闭。
有没有办法使用strncpy_s()
作为strncpy()
的实际替代品?
【问题讨论】:
【参考方案1】:您在这里面临的问题是您的函数本身是不安全的,就像strncpy()
一样。这是不安全的,因为您的函数的调用者可能会忘记返回的字符串不是以空值结尾的。如果这确实是您的函数所需的行为,我建议不要定义 _CRT_SECURE_NO_WARNINGS
并全局禁用警告,而是改用 #pragmas
:
// document here exactly why you can not use strncpy_s
#pragma warning( push )
#pragma warning( disable : 4996 )
// your code that uses strncpy instead of strncpy_s
#pragma warning( pop )
这样,您只能在绝对必须使用不安全函数的情况下禁用这些警告。
【讨论】:
【参考方案2】:您可以改用memcpy_s。
HRESULT someFunction( char* buffer, size_t length )
const char* toCopy = ...
size_t actualLength = strlen( toCopy );
if( actualLength > length )
return E_UNEXPECTED; // doesn't fit, can't do anything reasonable
else if ( actualLength < length )
actualLength++; // copy null terminator too
memcpy_s( buffer, length, toCopy, actualLength );
return S_OK;
【讨论】:
这不太对,因为它会在空终止符之后复制数据。正确的行为是在空终止符之前复制数据,并用零填充目标中的任何剩余空间。两个关键方面:(1)永远不会读取原始字符串末尾的数据;请注意,此类数据可能包含安全敏感信息; (2) 目标的所有元素都写入定义的值。 @supercatmemcpy_s
的最后一个参数用于确定要复制多少字节,所以它永远不会复制空终止符之后的数据。 (2) 确实,在空终止符之后的目标元素没有获得默认值,但在这个问题中没有问到这一点。在大多数软件中,无论如何都没有理由这样做。但我想这是一个不同的讨论。
我看错了代码;我错了为什么它不模仿 strncpy,但是如果程序需要 strncpy 功能,那么它不填零的事实将是一个问题。零填充字符串并不是特别深奥。它们通常用于将字符串存储到固定长度的记录中。如果代码要在记录上使用 memcpy() 或 fwrite() 之类的东西,则零填充很重要。【参考方案3】:
尝试将str*cpy*()
函数用于具有固定目标缓冲区的场景是一种常见的误解。这里的交易是这些函数“复制直到出现空字符或其他条件”。在这种情况下,有以下代码:
size_t actualLength = strlen( toCopy );
if( actualLength > length )
return E_UNEXPECTED; // doesn't fit, can't do anything reasonable
因此代码在继续复制之前知道实际的字符串长度。一旦知道长度,只有使用memcpy()
才有意义,这对于此类场景来说简单明了,而且副作用也更快,因为它允许一次复制多个字符并且不检查每个字符空终止符。
HRESULT someFunction( char* buffer, size_t length )
const char* toCopy = ...
size_t actualLength = strlen( toCopy );
if( actualLength > length )
return E_UNEXPECTED; // doesn't fit, can't do anything reasonable
memcpy( buffer, toCopy, min( length, actualLength + 1 ) );
return S_OK;
所以解决方案是忘记strncpy()
和strncpy_s()
并改用memcpy()
。
【讨论】:
【参考方案4】:您希望在actualLength < length
处以空值终止,而在actualLength == length
处不以空值终止,对吧?
所以对于actualLength < length
和memcpy_s
其中actualLength == length
的情况使用strncpy_s
。
【讨论】:
以上是关于如何使用 strncpy_s() 函数实现 strncpy() 功能?的主要内容,如果未能解决你的问题,请参考以下文章
求助ffmpeg.exe在windows自带的cmd下运行不了。显示无法定位程序输入点strncpy_s于动态链接库msvcrt.dll