在C中使用getline从stdin读取时如何避免内存泄漏?

kb5ga3dv  于 2023-03-01  发布在  其他
关注(0)|答案(4)|浏览(156)

我在C中使用getline从stdin读取时遇到内存泄漏,尽管确保释放了所有的mallocs,但在构建一个简单的shell时问题仍然存在。作为一个C新手,我正在寻找在使用getline进行文件输入时如何正确处理内存的建议。
这是我用来读这行的代码

char *readline(int *eof)
{
    char *input = NULL;
    size_t bufsize = 0;
    *eof = getline(&input, &bufsize, stdin);
    return (input);
}

这是实际的主要功能。

while (status)
    {
        mode = isatty(STDIN_FILENO);
        if (mode != 0)
        {
            write(STDOUT_FILENO, "$ ", 3);
        }
        line = readline(&eof);
        if (eof == -1)
        {
            exit(EXIT_FAILURE);
        }
        args = tokenize(line);
        status = hsh_execute(args, env, argv[0]);
        i = 0;
        while(args[i] != NULL)
        {
            free(args[i]);
            i++;
        }
        free(args);
        free(line);
    }

这是当我运行命令echo“/bin/ls”时valgrind返回的错误|./ shell

==33899== Invalid free() / delete / delete[] / realloc()
==33899==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899==    by 0x10980D: main (shell.c:40)
==33899==  Address 0x4a96040 is 0 bytes inside a block of size 120 free'd
==33899==    at 0x484B27F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899==    by 0x1097D5: main (shell.c:36)
==33899==  Block was alloc'd at
==33899==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==33899==    by 0x48EB1A2: getdelim (iogetdelim.c:62)
==33899==    by 0x1096CA: readline (readline.c:12)
==33899==    by 0x10976B: main (shell.c:26)
==33899== 
==33899== 
==33899== HEAP SUMMARY:
==33899==     in use at exit: 120 bytes in 1 blocks
==33899==   total heap usage: 4 allocs, 4 frees, 4,848 bytes allocated
==33899== 
==33899== LEAK SUMMARY:
==33899==    definitely lost: 0 bytes in 0 blocks
==33899==    indirectly lost: 0 bytes in 0 blocks
==33899==      possibly lost: 0 bytes in 0 blocks
==33899==    still reachable: 120 bytes in 1 blocks
==33899==         suppressed: 0 bytes in 0 blocks
==33899== Rerun with --leak-check=full to see details of leaked memory
==33899== 
==33899== For lists of detected and suppressed errors, rerun with: -s
==33899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关于realloc问题,这是我在tokenize函数中处理它的方式

#define BUFSIZE 64
#define DELIM " \t\r\n\a"
char **tokenize(char *line)
{
    /*declaration of various fucntions*/
    char **toks = malloc(sizeof(char *) * BUFSIZE);
    int position = 0;
    char *token, **token_backup;
    int bufsize = BUFSIZE;

    /*allocation error*/
    if (toks == NULL)
    {
        free(toks);
        perror("hsh");
    }
    token = strtok(line, DELIM);
    /*Store  token in toks*/
    while (token != NULL)
    {
        toks[position] = token;
        position++;
        /*not enough memory*/
        if (position >= BUFSIZE)
        {
            bufsize += BUFSIZE;
            token_backup = toks;
            toks = realloc(toks, position * sizeof(char *));
            if (toks == NULL)
            {
                free(token_backup);
                printf("allocation error\n");
                exit(EXIT_FAILURE);
            }
        }
        token = strtok(NULL, DELIM);
    }
    toks[position] = NULL;
    return (toks);
}
svdrlsy4

svdrlsy41#

你没有内存泄漏。你正在释放已经释放的内存。

i = 0;
    while(args[i] != NULL)
    {
        free(args[i]);
        i++;
    }
    free(args);
    free(line);

每个args[i]都是指向line内部某处的指针,它们不是单独分配的。
因此,摆脱循环和 * 只有 * 免费的argsline

zy1mlcev

zy1mlcev2#

我想这句台词

char *readline(int *eof)

应该是

char *readline(ssize_t *eof)

并且应当相应地声明eof

des4xlb0

des4xlb03#

我的猜测是正确的,你正在使用strtok。strtok将返回指向输入缓冲区的指针。所以argv[0]line相同。所以你实际上删除了line两次

kse8i1jr

kse8i1jr4#

tokenize函数不分配它返回的已分配数组中的单个字符串,这解释了line是如何被释放两次的:当i0free(line)位于main的末尾时,free(args[i])。此外,如果该行包含多个单词或以空格开头,则调用free时使用的指针指向已分配块内部,这具有未定义的行为。
tokenize函数也有问题:

  • 当你重新分配数组时,你传递了错误的大小:您应该传递bufsize * sizeof(char *)而不是position * sizeof(char *)

以下是修改后的版本:

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

/* getline wrapper with a different API */
ssize_t readline(char **linep) {
    size_t bufsize = 0;
    *linep = NULL;
    return = getline(linep, &bufsize, stdin);
}

#define BUFSIZE 64
#define DELIM " \t\r\n\a"
char **tokenize(char *line) {
    /* declaration of various variables */
    int bufsize = BUFSIZE;
    int position = 0;
    char **toks = malloc(sizeof(char *) * bufsize);
    char *token, **token_backup;

    if (toks == NULL) {
        /* allocation error */
        perror("cannot allocate array");
        exit(EXIT_FAILURE);
    }
    /* break line into tokens and store them in toks */
    token = strtok(line, DELIM);
    while (token != NULL) {
        toks[position] = token;
        position++;
        if (position >= BUFSIZE) {
            /* need more space */
            bufsize += BUFSIZE;
            token_backup = toks;
            toks = realloc(toks, sizeof(char *) * bufsize);
            if (toks == NULL) {
                free(token_backup);
                perror("cannot reallocate array");
                exit(EXIT_FAILURE);
            }
        }
        token = strtok(NULL, DELIM);
    }
    toks[position] = NULL;
    return toks;
}

char **env = NULL;
int hsh_execute(char **args, char **env, char *command);

int main(void) {
    int status = 1;

    while (status) {
        char *line;
        char **args;

        if (isatty(STDIN_FILENO)) {
            write(STDOUT_FILENO, "$ ", 3);
        }
        if (readline(&line) == -1) {
            // end of file reached
            exit(EXIT_FAILURE);
        }
        args = tokenize(line);
        status = hsh_execute(args, env, argv[0]);
        free(args);
        free(line);
    }
    return 0;
}

相关问题