C语言 重新写入拆分函数无法释放内存

nxowjjhe  于 2023-04-29  发布在  其他
关注(0)|答案(1)|浏览(175)

“目前有这个代码。它遵循一套必要的具体准则;因此,没有while循环或其他不是我们自己在头文件中创建的函数。
我写了一个函数,它将拆分一个字符串。只有外部函数。允许的是malloc和free。基本上,它分配(使用malloc(3))并返回一个字符串数组,该数组是通过使用字符'c'作为分隔符拆分's'获得的。数组必须以NULL指针结束。
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include "libft.h"

static int ft_length(char const *s, char c)
{
    size_t i;
    size_t words;

    i = 0;
    words = 0;
    while (s[i])
    {
       while (s[i] == c)
       {
            i++;
       }
        if (s[i] != c && s[i] != '\0')
        {
            words++;
        }
        while (s[i] != c && s[i] != '\0')
        {
            i++;
        }
    }
    return (words);
}

char    *ft_copy(char *s, char c)
{
    int     i;
    char    *cpy;

    i = 0;
    while (*s && *s == c)
        s++;
    while (s[i] && s[i] != c)
        i++;
    cpy = malloc(sizeof(char) * i + 1);
    if (cpy == NULL)
        return (NULL);
    i = 0;
    while (s[i] && s[i] != c)
    {
        cpy[i] = s[i];
        i++;
    }
    cpy[i] = '\0';
    return (cpy);
}

static void *cleanup(char **cpy, int j)
{
    int i = 0;
    while (i < j)
    {
        free(cpy[i]);
        i++;
    }
    return (0);
}

char **ft_split(char const *s, char c)
{
    size_t  i;
    char    **cpy;
    size_t  count;
    size_t  j;

    i = 0;
    j = 0;
    if (!s && !c)
        return (NULL);
    count = ft_length(s, c);
    cpy = malloc(sizeof(char *) *(count + 1));
    if (!cpy)
        return (NULL);
    while (s[i])
    {
        while (s[i] == c)
            i++;
        if ((int)i == ft_strlen((char *)s) && j == 0)
        {
            cpy[0] = 0;
            return (cpy);
        }
        if (s[i] != c)
        {
            cpy[j] = ft_copy((char *)&(s[i]), c);
            if (cpy[j] == NULL)
            {
                cleanup(cpy, j);
                return (NULL);
            }
            j++;
        }
        while (s[i] != c && s[i] != '\0')
            i++;
    }
    cpy[count] = NULL;
    return (cpy);
}

然而,当运行测试仪时,大约1字节的内存没有被正确释放,如以下行所示:

Error in test 3: ft_split("hello!zzzzzzzz", 122:'z'):
Memory leak: 0x55ae6a0998f0 - 1 bytes
You failed to free the memory allocated at:
ft_copy's malloc allocaton:
Error in test 4: ft_split("\\t\\t\\t\\thello!\\t\\t\\t\\t", 9:'\\t'):
Memory leak: 0x55ae6a09a920 - 1 bytes
You failed to free the memory allocated at:
ft_copy's malloc allocaton:
Error in test 7: ft_split("^^^1^^2a,^^^^3^^^^--h^^^^", 94:'^'):
Memory leak: 0x55ae6a09aa00 - 1 bytes
You failed to free the memory allocated at:
ft_copy's malloc allocaton:

想知道是否需要对代码进行全新的重写?然而,我似乎非常接近,只是不知道这个单一的字节,我不能释放是来自。
我希望所有的记忆都能被释放。

rjee0c15

rjee0c151#

  1. cleanup()应该释放cpy
  2. cleanup()需要一个计数j,但在正常情况下,你没有计数,所以删除参数,并依赖NULL
  3. ft_strlen()缺失,因此消除了它。你调用ft_strlen()的外部循环的每一次迭代,以确定我们是否在字符串的末尾。您只需检查s[i] == '\0'!s[i]即可。
    1.最小化变量的作用域。
    1.首选初始化变量。
    1.在遍历数组时,优先使用for()而不是while()
    1.(不固定)不要为不同的事情重复使用变量。这可能是或可能不是依赖性。最小化变量的作用域。
  4. NULL vs \0 vs 0
    1.(部分修复)首选对非负变量使用无符号类型(size_t)。
  5. (char *)&(s[i])很好,但s + i是一种更直接的说法。
  6. if ((int)i == ft_strlen((char *)s) && j == 0)意味着我们已经完成了字符串,所以只需要break以避免重复代码。
  7. if (s[i] != c)是冗余的。你知道的,它的情况下,否则前一个循环不会终止。
#include <stdio.h>
#include <stdlib.h>

static int ft_length(char const *s, char c) {
    size_t words = 0;
    for(size_t i = 0; s[i];) {
        for(; s[i] == c; i++);
        if (s[i] != c && s[i])
            words++;
        for(; s[i] != c && s[i]; i++);
    }
    return words;
}

char *ft_copy(const char *s, char c) {
    for(; *s && *s == c; s++);
    int i = 0;
    for(; s[i] && s[i] != c; i++);
    char *cpy = malloc(i + 1);
    if (!cpy)
        return NULL;
    i = 0;
    for(; s[i] && s[i] != c; i++)
        cpy[i] = s[i];
    cpy[i] = '\0';
    return cpy;
}

static void cleanup(char **cpy) {
    if(!cpy)
        return;
    for(int i = 0; cpy[i]; i++)
        free(cpy[i]);
    free(cpy);
}

char **ft_split(char const *s, char c) {
    if (!s && !c)
        return NULL;
    size_t count = ft_length(s, c);
    char **cpy = malloc(sizeof(char *) * (count + 1));
    if (!cpy)
        return NULL;
    size_t j = 0;
    for(size_t i = 0; s[i]; ) {
        for(; s[i] == c; i++);
        if (!s[i])
            break;
        cpy[j] = ft_copy(s + i, c);
        if (!cpy[j]) {
            cleanup(cpy);
            return NULL;
        }
        j++;
        for(; s[i] != c && s[i]; i++);
    }
    cpy[j] = NULL;
    return cpy;
}

int main(void) {
    struct tests {
        char *str;
        char c;
    } tests[] = {
        {"hello!zzzzzzzz", 'z'},
        {"\t\t\t\thello!\t\t\t\t", '\t'},
        {"^^^1^^2a,^^^^3^^^^--h^^^^", '^'}
    };
    for(struct tests *t = tests; t < &tests[sizeof tests / sizeof *tests]; t++) {
        char **s = ft_split(t->str, t->c);
        if(!s)
            return 1;
        for(size_t i = 0; s[i]; i++)
            printf("%zu: %s\n", i, s[i]);
        cleanup(s);
    }
}

示例运行:

$ valgrind ./a.out 
==14199== Memcheck, a memory error detector
==14199== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14199== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==14199== Command: ./a.out
==14199== 
0: hello!
0: hello!
0: 1
1: 2a,
2: 3
3: --h
==14199== 
==14199== HEAP SUMMARY:
==14199==     in use at exit: 0 bytes in 0 blocks
==14199==   total heap usage: 10 allocs, 10 frees, 1,122 bytes allocated
==14199== 
==14199== All heap blocks were freed -- no leaks are possible
==14199== 
==14199== For lists of detected and suppressed errors, rerun with: -s
==14199== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

相关问题