You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C语言getline读取stdin时如何避免内存泄漏及无效释放问题

简易Shell中getline内存处理问题及修复

我用C语言编写简易Shell时,使用getline从stdin读取输入出现内存错误,尽管已经释放所有malloc分配的内存,问题仍存在。作为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);
}

Valgrind错误信息

执行 echo "/bin/ls" | ./shell 命令时,Valgrind返回如下错误:

==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)

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);
}

核心问题分析

  1. 重复释放内存:strtok直接在原字符串line上分割,返回的指针是line的子地址,并非独立分配的内存。主函数中先free(args[i])再free(line),相当于重复释放同一块内存,触发Valgrind的Invalid free()错误。
  2. getline返回值判断错误:主函数中if (eof == -1)是错误的,eof是指针,应该判断*eof == -1;且getline返回-1时,可能已分配了部分内存,未释放会导致泄漏。
  3. tokenize扩容参数错误:realloc时用position而非bufsize计算内存大小,会导致分配的内存不足,存在越界风险。
  4. tokenize错误处理冗余:toks == NULL时调用free(toks)无效,NULL不需要释放。

修复步骤

1. 修正主函数的内存释放与返回值判断

移除对args[i]的释放(因为它们是line的一部分),修正getline返回值的判断逻辑:

while (status)
{
    mode = isatty(STDIN_FILENO);
    if (mode != 0)
    {
        write(STDOUT_FILENO, "$ ", 3);
    }
    line = readline(&eof);
    // 修正:判断指针指向的返回值,而非指针本身
    if (*eof == -1)
    {
        // 若getline失败但已分配内存,需释放
        if (line != NULL)
            free(line);
        exit(EXIT_FAILURE);
    }
    args = tokenize(line);
    status = hsh_execute(args, env, argv[0]);
    // 仅释放args数组本身,不需要释放args[i]
    free(args);
    free(line);
}

2. 修正tokenize函数的问题

移除冗余的free(toks),修正realloc的内存大小参数:

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

    if (toks == NULL)
    {
        perror("hsh");
        exit(EXIT_FAILURE);
    }
    token = strtok(line, DELIM);
    while (token != NULL)
    {
        toks[position] = token;
        position++;
        // 用bufsize判断是否需要扩容
        if (position >= bufsize)
        {
            bufsize += BUFSIZE;
            token_backup = toks;
            // 修正:用bufsize计算总内存大小
            toks = realloc(toks, bufsize * sizeof(char *));
            if (toks == NULL)
            {
                free(token_backup);
                perror("allocation error");
                exit(EXIT_FAILURE);
            }
        }
        token = strtok(NULL, DELIM);
    }
    toks[position] = NULL;
    return (toks);
}

3. getline内存处理规范

  • 调用getline前,确保传入的指针为NULL(当前readline函数已满足);
  • getline返回-1时,必须检查返回的指针是否非空,若已分配内存则释放;
  • 成功读取后,使用完毕必须释放getline返回的指针。

内容的提问来源于stack exchange,提问作者New

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 02:19:59