【问题标题】:Why does my implementation of strlcat gives a segmentation fault when the original does not?为什么我的 strlcat 实现会出现分段错误,而原来的却没有?
【发布时间】:2022-09-27 19:03:27
【问题描述】:

我正在编写 strlcat 的重新实现作为练习。我已经进行了几次测试,它们产生了类似的结果。但是在一个特殊情况下,我的函数给出了分段错误错误,而原来的函数没有,你能解释一下为什么吗?我不允许使用任何标准库函数,这就是我重新实现 strlen() 的原因。

这是我写的代码:

#include <stdio.h>
#include <string.h>

int ft_strlen(char *s)
{
    int i;

    i = 0;
    while (s[i] != \'\\0\')
        i++;
    return (i);
}

unsigned int    ft_strlcat(char *dest, char *src, unsigned int size)
{
    size_t  i;
    int     d_len;
    int     s_len;

    i = 0;
    d_len = ft_strlen(dest);
    s_len = ft_strlen(src);
    if (!src || !*src)
        return (d_len);
    while ((src[i] && (i < (size - d_len - 1))))
    {
        dest[i + d_len] = src[i];
        i++;
    }
    dest[i + d_len] = \'\\0\';
    return (d_len + s_len);
}

int main(void)
{
    char s1[5] = \"Hello\";
    char s2[] = \" World!\";

printf(\"ft_strcat :: %s :: %u :: sizeof %lu\\n\", s1, ft_strlcat(s1, s2, sizeof(s1)), sizeof(s1));
    // printf(\"strlcat :: %s :: %lu :: sizeof %lu\\n\", s1, strlcat(s1, s2, sizeof(s1)), sizeof(s1));
}

使用 strlcat 的输出是:strlcat :: Hello World! :: 12 :: sizeof 5。我在 macOS 上,如果有帮助的话,我正在使用 clang 进行编译。

  • 可能与sizeof 有关,字符串缓冲区总是比字符串缓冲区的strlen() 返回的长度大1。在调试器中单步执行代码时,仔细检查 dest[i + d_len] = src[i];while ((src[i] &amp;&amp; (i &lt; (size - d_len - 1)))) 中的 i 的值。
  • 使用size_t 而不是intunsigned int
  • s1[5] 数组不足以容纳 \"Hello\" - 这需要 6 个字符(一个用于空终止符)。
  • 声明 char s1[5] = \"Hello\";有效的(在 C 中)但它不会在数组中包含 nul 终止符。那是必需的对于几乎所有使用字符串的 C 函数。
  • 特别是,strlcat() 需要它,因为strlen(dest) 会查找它。

标签: c function segmentation-fault


【解决方案1】:

ft_strlcat() 还不错,但它希望指针指向字符串. main() 很麻烦:s1 缺一个空字符:所以s1 不是细绳.

//char s1[5] = "Hello";
char s1[] = "Hello"; // Use a string

s1[] 对于连接的字符串 "HelloWorld" 来说太小了

char s1[11 /* or more */] = "Hello"; // Use a string

"%lu" 匹配 unsigned long。来自sizeofsize_t 匹配"%zu"


一些ft_strlcat() 问题:

unsigned, intsize_t

unsigned, int 太窄了字符串。使用size_t 处理所有字符串.

测试太晚了

if (!src || ...) 为时已晚,因为之前的 ft_strlen(src);src == NULL 时调用 UB。

const

ft_strlcat() 应使用指向 const 的指针,以允许将 const 字符串与 src 一起使用。

高级:restrict

使用restrict,这样编译器就可以假设dest, src 不重叠并发出更高效的代码——假设它们不应该重叠。

角落案例

它不能处理像d_len &gt;= size 那样的一些讨厌的极端情况,但我将把详细的分析留到以后。


建议签名

// unsigned int    ft_strlcat(char *dest, char *src, unsigned int size)
size_t ft_strlcat(char * restrict dest, const char * restrict src, size_t size)

一些未经测试供您考虑的代码:

  • 试图模仿strlcat()

  • 返回字符串长度的总和,但不会超过 size

  • 不检查超过size 个字符以防止读取越界。

  • 不附加一个空字符当没有足够的空间。

  • 不检查 dst, src 作为 NULL。喜欢就加吧。

  • 不处理重叠的destsrc。除非库例程可用,否则这样做很棘手。

  • 使用unsigned char * 指针正确处理罕见的有符号非2 补码char

size_t my_strlcat(char * restrict dst, const char * restrict src, size_t size) {
  const size_t size_org = size;

  // Walk dst
  unsigned char *d = (unsigned char*) dst;
  while (size > 0 && *d) {
    d++;
    size--;
  }
  if (size == 0) {
    return size_org;
  }

  // Copy src to dst
  const unsigned char *s = (const unsigned char*) src;
  while (size > 0 && *s) {
    *d++ = *s++;
    size--;
  }
  if (size == 0) {
    return size_org;
  }

  *d = '\0';
  return (size_t) (d - (unsigned char*) dst);
}

如果返回值小于size,则成功!

【讨论】:

    【解决方案2】:
    1. s1 甚至不足以容纳“你好”

    2. 使用正确的尺寸类型。

      size_t ft_strlcat(char *dest, const char *src, size_t len)
      {
          char *savedDest = dest;
          if(dest && src && len)
          {
              while(*dest && len)
              {
                  len--;
                  dest++;
              }
              if(len)
              {
                  while((*dest = *src) && len)
                  {
                      len--;
                      dest++;
                      *src++;
                  }
              }
              if(!len) dest[-1] = 0;
          }
          return dest ? dest - savedDest : 0;
      }   
      
      1. 此外,您的printf 调用未定义的行为,因为函数参数评估的顺序未确定。它应该是:
      int main(void)
      {
          char s1[5] = "Hello";     //will only work for len <= sizeof(s1) as s1 is not null character terminated
          char s2[] = " World!";
      
          size_t result = ft_strlcat(s1, s2, sizeof(s1));
          printf("ft_strcat :: %s ::  %zu :: sizeof %zu\n", s1, result, sizeof(s1));
      }
      
      

      https://godbolt.org/z/8hhbKjsbx

    【讨论】:

    • @chux-ReinstateMonica 向另一个人询问了 (uintptr_t)NULL - (uintptr_t)NULL
    • @chux-ReinstateMonica 问题“可能”
    【解决方案3】:

    你好我有一个关于这个的问题:

    if (!src || !*src) 返回(d_len);

    我不明白任何人都可以向我解释一下,谢谢

    【讨论】:

      猜你喜欢
      • 2018-01-07
      • 2018-01-28
      • 2011-04-23
      相关资源
      最近更新 更多