我尝试创建一个函数strlcpy,我将其命名为ft_strlcpy。我的函数是否像这样工作strlcpy

size_t  ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
    size_t  len;

    len = ft_strlen(src);
    if (dstsize)
    {
        while (dstsize-- > 1 && *src)
            *dst++ = *src++;
        *dst = '\0';
    }
    return (len);
}

我尝试询问claud ai,但是它告诉我功能不完全相同。

9

  • 1
    “我的函数像 ft_strlcpy 一样工作吗?”当然 ft_strlcpy 可以像 ft_strlcpy 一样工作..:)


    – 


  • 首先,请尝试创建一个,并您的问题以显示它。并且在您问题时,还请告诉我们您期望的行为以及您实际得到的行为。


    – 


  • 这将是学习自动化单元测试的好机会。尝试考虑可能出错的尽可能多的可能性,编写代码来调用您的函数,然后使用类似的东西memcmp来检查结果。


    – 

  • 你询问的 AI 可能至少访问过 的一个实现strlcpy,并由此得出结论,你的函数并不完全相同。除此之外,(假设size_t (*ft_strlen)(const char*) = strlen;),我无法发现结果有什么不同


    – 


  • 1
    具有符合 要求的指针restrict


    – 



最佳答案
2

以下是原始 BSD 手册页中关于strlcpy(和)的规范:strlcat

概要

#include <bsd/string.h>

size_t strlcpy(char *dst, const char *src, size_t size);
size_t strlcat(char *dst, const char *src, size_t size);

描述

strlcpy()函数strlcat()分别复制和连接字符串。它们旨在成为strncpy(3)和 的更安全、更一致、更不容易出错的替代品strncat(3)。与那些函数不同,strlcpy()strlcat()占用缓冲区的完整大小(而不仅仅是长度)并保证以 NUL 终止结果(只要 size 大于 0,或者在 的情况下strlcat(),只要 中至少有一个可用字节dst)。请注意,NUL 的字节应包含在 size 中。另请注意strlcpy()strlcat()仅对真正的“C”字符串进行操作。这意味着strlcpy() src必须以 NUL 终止,并且strlcat()必须以 NUL 终止。srcdst

该函数从以 NUL 结尾的字符串复制strlcpy()最多个字符,并以 NUL 结尾的结果。size - 1srcdst

strlcat()函数将以 NUL 结尾的字符串附加src到 的末尾dst。它将附加最多size - strlen(dst) - 1字节,并以 NUL 结尾结果。

返回值

strlcpy()函数strlcat()返回它们尝试创建的字符串的总长度。对于 ,strlcpy()这意味着 的长度src。对于,strlcat()这意味着 的初始长度dst加上 的长度src。虽然这看起来有些令人困惑,但这样做是为了让截断检测变得简单。

但请注意,如果strlcat()遍历size字符而未找到 NUL,则字符串的长度将被视为 NUL size,并且目标字符串将不会以 NUL 结尾(因为没有 NUL 的空间)。这可以防止strlcat()超出字符串的末尾。实际上,这种情况不应该发生(因为这意味着 是size错误的,或者dst不是一个正确的“C”字符串)。检查是为了防止错误代码中出现潜在的安全问题。

现在关于你的问题:我的功能是否像strlcpy

  • 它确实会返回strlen(src)
  • 它复制的字符不超过从 到 NUL 终止符的dstsize-1字符。src
  • 它会以 NUL 终止dst,前提dstsize是 不是0

您对第三个参数使用了不同的名称,但这并没有带来功能上的差异。

但请注意,您没有提供 的源代码ft_strlen,只能假定它返回字符串的长度,即:表现得像strlen

因此,从我的分析来看,您的函数确实表现得像strlcpy。无论您要求的建议是什么,它似乎都是不正确的,如果您这样告诉它,它可能会同意并告诉您原因。


@pmg 评论说 POSIX 标准化版本的规范与strlcpy中指定的略有不同

#include <string.h>

size_t strlcat(char *restrict dst, const char *restrict src,
       size_t dstsize);
size_t strlcpy(char *restrict dst, const char *restrict src,
       size_t dstsize);

限定符restrict告诉编译器关于参数的附加约束,指定为如果在重叠的对象之间进行复制,则行为未定义。 这允许编译器假定通过执行的修改dst不能更改通过访问的字节src

这本身不会改变实现的行为,BSD 手册页中未指定源区域和目标区域重叠的情况,这可能会造成混淆或错误的预期。


可能引发混乱的是你不寻常的循环

while (dstsize-- > 1 && *src)
    *dst++ = *src++;

虽然正确,但可以写成更易读的风格

// copy no more than dstsize - 1 bytes from NUL-terminated src
//   note: dstsize is known to be > 0
for (size_t count = 0; count < dstsize - 1 && *src; count++)
    *dst++ = *src++;

一些有创造力的人甚至可能更喜欢:

while (dstsize --> 1 && *src)
    *dst++ = *src++;

的话题


另一点是您的实现读取源字符串两次:一次测量长度,第二次至少部分复制适合目标区域的部分。虽然这对调用者来说不是一个可见的差异,但可以说这种 2 遍方法效率低下。以下是使用单遍的替代方法:

size_t  ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
    size_t  i = 0;

    if (dstsize) {
        // copy no more than dstsize - 1 bytes
        while (i < dstsize - 1) {
            char c = src[i];
            dst[i] = c;
            // early stop if the string fits in the destination
            if (c == '\0')
                return i;
            i++;
        }
        dst[i] = '\0';
    }
    // count the remaining bytes in the source string
    while (src[i] != '\0')
        i++;
    return i;
}

1

  • 关于“读取源字符串两次”的观点很好。


    – 

我希望ft_strlcpy()使用restrict指针来指示重叠内存存在未定义行为(UB)的风险。

size_t ft_strlcpy(char *restrict dst, const char *restrict src, size_t dstsize);

如果代码确实要在没有restrict(的情况下工作,src并且dst可能指向重叠区域),那么请使用初始的len。OP 的代码,按原样,在dst == src + 1等情况下会遇到麻烦。

size_t ft_strlcpy(char *dst, const char *src, size_t dstsize) {
    size_t len = ft_strlen(src);

    if (dstsize > 0) {
      size_t copy = min(len, dstsize - 1);
      memmove(dst, src, copy);  // Handles overlapping memory.
      dst[copy] = '\0';
    }
    return len;
}

2

  • 1
    良好的替代实现,完全定义了重叠区域。在 OP 的上下文中(42、Epitech、Epita…),memmove可能不允许,但他们可以使用自己的重新实现ft_memmove,但根据 C 标准,以完全定义的方式编写它出奇地困难。


    – 


  • 1
    同意_出乎意料的困难_。


    –