【问题标题】:My own substring function | valgrind showing some malloc errors I do not understand我自己的子字符串函数 | valgrind 显示一些我不明白的 malloc 错误
【发布时间】:2021-03-02 19:33:31
【问题描述】:

任务:

分配(使用 malloc(3))并从字符串 s 返回一个子字符串。子字符串从索引start 开始,最大大小为len

返回值:子字符串。 NULL 如果分配失败。

您好,几个小时后,我决定要求澄清一下。我有以下功能和一些我无法理解的 Valgrind 错误,即使一切都正确,也会出现。 (ft_strlen(s)我从我自己的库中调用,其中还放了 malloc 的 lib)。

char    *ft_substr(char const *s, unsigned int start, size_t len)
{
    unsigned int x;
    char        *a;
    unsigned int i;

    i = 0;
    if (s == NULL)
        return (0);
    if (start > ft_strlen(s))
    {   
        if (!(a = (char *)malloc(0*sizeof(char))))
            return (0);
        return (a);
    }
    if ((start + len) < ft_strlen(s))
        x = len;
    else
        x = ft_strlen(s) - start;
    if (!(a = (char *)malloc((x + 1) * sizeof(char))))
        return(0);
    while (i < x)
    {
        a[i] = s[start + i];
        i++;
    }
    a[i] = '\0';
    return (a);
}

我故意留下一个错误。如果我想在分配失败时返回 null,为什么下面应该是 0 而不是 1?无论如何,它不会改变下面出现的错误。

if (!(a = (char *)malloc(0 * sizeof(char))))

错误:

==4817== Invalid read of size 1
==4817==    at 0x483FED4: strcmp (in /usr/lib/x86_64-linux-gnu/valgrind vgpreload_memcheck-amd64-linux.so)
==4817==    by 0x4039BC: main (ft_substr_test.cpp:28)
==4817==  Address 0x4dad0d0 is 0 bytes after a block of size 0 alloc'd
==4817==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==4817==    by 0x403B58: ft_substr (in /home/tony/42cursus/0lvl_libft_1week/libftTester/a.out)
==4817==    by 0x4039A4: main (ft_substr_test.cpp:27)
==4817==

【问题讨论】:

  • 当您malloc(0) 时,结果可以是 NULL,也可以是您无法使用的有效指针。从错误消息来看,您似乎得到了后者,并且您将指针传递给了strcmp。在 malloc 之后,尝试使用 printf("%p\n", (void *)a); 打印指针
  • 代码应该是if (s == NULL || start &gt; ft_strlen(s)) return NULL;
  • 不,我试过了,但没用@user3386109
  • 这段代码既不完整也不是最小的 - 请发布一个完整的程序(main()),它刚好来演示问题,仅此而已。

标签: c malloc valgrind


【解决方案1】:

你的函数有多个问题:

  • startxi 的类型应为size_t
  • malloc(0) 具有实现定义的行为。您应该为空终止符分配至少 1 个字节并在返回指向空字符串的指针之前设置它,或者如果规范要求您应该返回 NULL
  • 该函数应该只调用一次ft_strlen()
  • start &gt; ft_strlen(s) 的特殊情况可以在一般情况下处理,如果应该返回一个空字符串。

这是修改后的版本:

char *ft_substr(char const *s, size_t start, size_t len) {
    size_t i, slen;
    char *a;

    if (s == NULL) {
        return NULL;
    }
    slen = ft_strlen(s);
    if (start > slen) {
        start = slen;
    }
    if (len > slen - start) {
        len = slen - start;
    }
    if (!(a = malloc((len + 1) * sizeof(char)))) {
        return NULL;
    }
    for (i = 0; i < len; i++) {
        a[i] = s[start + i];
    }
    a[i] = '\0';
    return a;
}

PS:您可能需要重新格式化代码以适应本地 42 norminette...

【讨论】:

  • 谢谢!我知道出了什么问题,大小为 1 的 a 必须以 '\0' 终止,因此出现错误。关于开始。 x 和 i。该函数在手册中是这样定义的:..(unsigned int start, size_t len) 这就是我这样写的原因;)。但是非常感谢你的回答,我现在分析一下,看看如何把我的代码写得更漂亮!
【解决方案2】:

在 Linux 系统上,调用 malloc(0) 不一定会返回 NULL 指针。它可以返回一个您无法写入但可以传递给free 的指针。

因此,当您从函数返回 malloc(0) 的结果时,调用函数会看到一个非空指针并尝试取消引用它。由于这个指针本质上指向一个大小为 0 的缓冲区,因此尝试读取它会超过缓冲区的末尾,这就是 valgrind 所抱怨的。

您可以通过返回 NULL 来解决此问题:

if (start > ft_strlen(s))
{   
    return NULL;
}

或者通过为空字符串分配空间并设置空字节:

if (start > ft_strlen(s))
{   
    if (!(a = malloc(1)))
        return NULL;
    *a = 0;
    return a;
}

其他几点说明:

  • sizeof(char) 定义为 1,因此您可以将其排除在大小计算之外
  • 不要转换malloc的返回值。
  • 对空指针使用 NULL 而不是 0
  • return 语句中的表达式不需要括号。

【讨论】:

  • 谢谢!!我现在明白我猜的问题了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多