strncat和strncpy之间是否有任何区别来编写安全的字符串复制功能?

时间:2016-01-14 02:15:30

标签: c string c89

我遇到了两种流行的方法来编写一个可移植的安全复制功能,并且符合C89。

示例1:

strncpy(dst, src, size);
dst[size - 1] = '\0';

示例2:

dst[0] = '\0'
strncat(dst, src, size - 1);

我的问题:

  1. 这两者之间是否存在任何技术差异?
  2. 是否有任何特殊情况,其中一个优先于另一个?

2 个答案:

答案 0 :(得分:2)

是的,它们在技术上是不同的。虽然你可能不关心微不足道的差异。

e.g。如果你这样初始化:

npm install email-validator --save

然后使用“示例1”,char dst[] = "abcdefg"; char src[] = "12"; size_t size = sizeof dst; 变为dst

使用“示例2”,0x31 0x32 0x00 0x00 0x00 0x00 0x00 0x00变为dst

如果您只想复制字符串,那么差异无关紧要。

很难说哪一个更好。但是在非常大0x31 0x32 0x00 0x64 0x65 0x66 0x67 0x00和非常短size的情况下,使用“示例1”方法设置所有尾随空字符可能会使程序变慢。

答案 1 :(得分:-1)

https://en.cppreference.com/w/cpp/string/byte/strncat
https://en.cppreference.com/w/cpp/string/byte/strncpy

这是一个 C++(哈哈哈)的例子,说明了不同之处:
https://wandbox.org/permlink/igKe0CtdyuMw1JHL

#include <iostream>
#include <cstring>
#include <cassert>

//////////////////////////////////////////////////////////
// using strncat
// https://en.cppreference.com/w/cpp/string/byte/strncat

template <std::size_t N>
inline void safestrcpy1(char *dest, const char *src)
{
    constexpr std::size_t count = N-1;
    dest[0] = '\0';
    strncat(dest, src, count); // at most count characters are copied from string src; 
                               // and then a terminating null character '\0' is written if it was not yet encountered
}

template <std::size_t N>
inline void safestrcpy1(char (&dest)[N], const char *src)
{
    safestrcpy1<N>(&dest[0], src);
}

//////////////////////////////////////////////////////////
// using strncpy
// https://en.cppreference.com/w/cpp/string/byte/strncpy

template <std::size_t N>
inline void safestrcpy2(char *dest, const char *src)
{
    constexpr std::size_t count = N-1;
    strncpy(dest, src, count); // null termination '\0' is only written if it occurs somewhere within src[0] .. src[count-1]
                               // if it occurs at src[i] and i < count, then dest[i+1], dest[i+2], ... dest[count-1] are also set to '\0'.
    dest[count] = '\0';        // ensure null termination
}

template <std::size_t N>
inline void safestrcpy2(char (&dest)[N], const char *src)
{
    safestrcpy2<N>(&dest[0], src);
}


//////////////////////////////////////////////////////////
// main
//
int main()
{
    {
        char dest1[4] = "xyz";
        char dest2[4] = "xyz";
        safestrcpy1(dest1, "");
        safestrcpy2(dest2, "");
#define RES1A "\0yz"
#define RES2A "\0\0\0"
        assert(memcmp(dest1, RES1A, sizeof(RES1A)) == 0);
        assert(memcmp(dest2, RES2A, sizeof(RES2A)) == 0);
        std::cout << dest1 << std::endl;
    }
    {
        char dest1[4] = "xyz";
        char dest2[4] = "xyz";
        safestrcpy1(dest1, "1");
        safestrcpy2(dest2, "1");
#define RES1B "1\0z"
#define RES2B "1\0\0"
        assert(memcmp(dest1, RES1B, sizeof(RES1B)) == 0);
        assert(memcmp(dest2, RES2B, sizeof(RES2B)) == 0);
        std::cout << dest1 << std::endl;
    }
    {
        char dest1[4] = "xyz";
        char dest2[4] = "xyz";
        safestrcpy1(dest1, "12");
        safestrcpy2(dest2, "12");
#define RES1C "12\0"
#define RES2C "12\0"
        assert(memcmp(dest1, RES1C, sizeof(RES1C)) == 0);
        assert(memcmp(dest2, RES2C, sizeof(RES2C)) == 0);
        std::cout << dest1 << std::endl;
    }
    {
        char dest1[4] = "xyz";
        char dest2[4] = "xyz";
        safestrcpy1(dest1, "123");
        safestrcpy2(dest2, "123");
#define RES1D "123"
#define RES2D "123"
        assert(memcmp(dest1, RES1D, sizeof(RES1D)) == 0);
        assert(memcmp(dest2, RES2D, sizeof(RES2D)) == 0);
        std::cout << dest1 << std::endl;
    }
    {
        char dest1[4] = "xyz";
        char dest2[4] = "xyz";
        safestrcpy1(dest1, "1234");
        safestrcpy2(dest2, "1234");
#define RES1E "123"
#define RES2E "123"
        assert(memcmp(dest1, RES1E, sizeof(RES1E)) == 0);
        assert(memcmp(dest2, RES2E, sizeof(RES2E)) == 0);
        std::cout << dest1 << std::endl;
    }
}