关于c ++:如何使用strncpy_s()函数实现strncpy()功能?

How to achieve strncpy() functionality with strncpy_s() function?

在某些情况下,我确实需要strncpy()功能-例如,我在预定义的接口中有一个函数,该函数传递了缓冲区的地址和缓冲区的大小:

1
HRESULT someFunction( char* buffer, size_t length );

据记录,我可以在其中复制长度不超过length的以空终止的字符串-如果长度恰好是length,则我不以空终止该字符串,并且调用方知道该字符串以一个空字符或长度为length的那个,以先发生的情况为准。

当然我会使用strncpy()

1
2
3
4
5
6
7
8
9
10
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。我对其进行编译,并看到一条警告,提示strncpy()是不安全的,我应该改用strncpy_s()。

strncpy_s()旨在始终以null终止缓冲区,因此在上述情况下我不能将其用作直接替换。我必须在长度大于length - 1的字符串上返回E_UNEXPECTED(而不是以前的length),否则一旦字符串为length或更长,它将仅触发无效的参数错误处理程序未定义的行为。

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

有什么方法可以使用strncpy_s()作为strncpy()的实际替代品?


您在这里面临的问题是您的函数本身不安全,就像strncpy()一样。这是不安全的,因为函数的调用者可能会忘记返回的字符串不是以null结尾的。如果这确实是您的函数所希望的行为,则建议不要定义_CRT_SECURE_NO_WARNINGS并全局禁用警告,而应使用#pragmas

1
2
3
4
5
// 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 )

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


您可以改用memcpy_s。

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}


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

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

因此代码在进行复制之前就知道了实际的字符串长度。一旦知道了长度,就可以使用memcpy()了,这对于这种情况是简单明了的,并且副作用也更快,因为允许一次复制多个字符并且不检查每个字符的长度空终止符。

1
2
3
4
5
6
7
8
9
10
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()


您想在actualLength < length处使用空终止,而在actualLength == length处不使用空终止,对吗?

因此,对于actualLength < lengthmemcpy_s其中actualLength == length的情况,请使用strncpy_s