【问题标题】:Is there any difference between strncat and strncpy to write safe string copy function?strncat 和 strncpy 编写安全的字符串复制功能有什么区别吗?
【发布时间】:2016-04-19 05:56:16
【问题描述】:

我遇到了两种流行的方法来编写可移植且符合 C89 的安全复制函数。

示例 1:

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

示例 2:

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

我的问题:

  1. 两者之间是否存在技术差异?
  2. 是否存在任何一种优先于另一种的特殊情况?

【问题讨论】:

  • 保证它们的行为方式相同,因此它们都更好。
  • 你也可以试试 snprintf,它会给你一个稍微有用的返回值,即写入的字符总数。
  • @M.M 如果sizedst 的缓冲区大小,那么size - 1 是正确的。
  • @Griddoor 好点。所以一个潜在的问题是,如果size == 0,两者都不能正常工作;而size在每个例子中都有不同的含义
  • @MadPhysicist 他们的行为方式不同。有关详细信息,请参阅接受的答案。这就是我在他们之间寻找的那种差异。

标签: c string c89


【解决方案1】:

是的,它们在技术上是不同的。虽然你可能不在乎这些微不足道的区别。

例如如果你这样初始化:

char dst[] = "abcdefg";
char src[] = "12";
size_t size = sizeof dst;

然后使用您的“示例 1”,dst 变为 0x31 0x32 0x00 0x00 0x00 0x00 0x00 0x00

使用您的“示例 2”,dst 变为 0x31 0x32 0x00 0x64 0x65 0x66 0x67 0x00

如果你只想复制字符串,那么区别并不重要。

很难说哪个更好。但是对于非常大的size 和非常短的src,使用“示例 1”方法设置所有尾随空字符可能会使程序变慢。

【讨论】:

  • @M.M 我不知道大写被认为是坏的。还是修改了。
【解决方案2】:

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;
    }
}

【讨论】:

    猜你喜欢
    • 2011-01-01
    • 2019-03-12
    • 2010-09-13
    • 2011-02-26
    • 2010-10-01
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多