如何使用strncpy_s()函数实现strncpy()功能?

时间:2011-02-18 07:18:22

标签: c++ visual-c++ string

在某些情况下我确实需要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()旨在始终为空终止缓冲区,因此我无法在上述场景中将其用作直接替换。我必须在长度超过E_UNEXPECTED的字符串上返回length - 1(而不是之前的length),或者只要字符串为length,它就会触发无效的参数错误处理程序或更长时间或程序将遇到未定义的行为。

到目前为止我应用的解决方案是定义一个_CRT_SECURE_NO_WARNINGS并使编译器闭嘴。

有没有办法使用strncpy_s()作为strncpy()的实际替代品?

4 个答案:

答案 0 :(得分:4)

你在这里遇到的问题是你的功能本身就不安全,就像strncpy()一样。它是不安全的,因为函数的调用者可能会忘记返回的字符串不是以null结尾。如果这确实是您的功能所需的行为,我建议您不要定义_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 ) 

这样,您只能在绝对必须使用不安全功能的情况下禁用这些警告。

答案 1 :(得分:3)

您可以改用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;
}

答案 2 :(得分:2)

尝试将str*cpy*()函数用于具有固定目标缓冲区的方案是一种常见的误解。这里的交易是那些函数“复制直到出现空字符或其他条件”。在这种情况下,就有了这个代码:

size_t actualLength = strlen( toCopy );
if( actualLength > length ) {
    return E_UNEXPECTED; // doesn't fit, can't do anything reasonable 
}

因此代码在进行复制之前知道实际的字符串长度。一旦知道了长度,只使用memcpy()才有意义,这对于这样的场景既简单又简洁,副作用也更快,因为它允许一次复制多个字符而不检查每个null终止符的字符。

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()

答案 3 :(得分:1)

你想要actualLength < length的空终止,而actualLength == length没有空终止,对吗?

因此strncpy_sactualLength < length memcpy_s的情况下使用actualLength == length