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

C语言get_next_line函数静态字符内存泄漏修复求助

修复get_next_line中静态字符数组的内存泄漏问题

问题背景

我正在实现get_next_line函数,功能是从文件描述符读取一行内容并返回存储该行的字符数组。实现逻辑如下:

  • 将读取到的数据存入缓冲区,每次读取后合并到结果数组
  • 若遇到换行符,截取换行符前的内容作为结果返回,剩余数据存入静态数组供下次调用使用

目前函数输出结果正确,但存在静态字符数组相关的内存泄漏,其余泄漏已解决。开发限制:仅允许使用read()、malloc()、free()函数,其余函数自行实现(代码中前缀ft_为自定义函数标识)。

原代码

#include "get_next_line.h"

char * get_next_line(int fd) {

   // 静态变量reminder存储换行符后的剩余内容,供下次读取使用
  static char *reminder = "";
  char buf[BUFFER_SIZE + 1];
  int nbytes;
  char * line = malloc(BUFFER_SIZE + 1);
  if (!line || reminder == NULL) {
    return NULL;
  }

  if (fd < 0 || fd > 999) {
    free(line);
    return NULL;
  }

  while ((nbytes = read(fd, buf, BUFFER_SIZE)) > 0) {
    buf[nbytes] = '\0';
    // ft_strjoin:分配内存并返回s1和s2拼接后的新字符串
    reminder = ft_strjoin(reminder, buf);
    // 自定义ft_strchr:返回换行符之后的字符指针
    if (ft_strchr(reminder, '\n')) {
      // 截取reminder中从开头到换行符的内容作为行
      line = ft_substr(reminder, 0, '\n');
      reminder = ft_strchr(reminder, '\n');
      return line;
    }
  }
  if (ft_strcmp(reminder, "") == 0 || nbytes < 0 || reminder == NULL) {
    free(line);
    return NULL;
  }

   if ((!(ft_strchr(reminder, '\n')))) {
      char *ptr = reminder;
      reminder = NULL;
      return ptr;
 }

   if (ft_strchr(reminder, '\n')) {
      char *ptr = ft_substr(reminder, 0, '\n');
      reminder = ft_strchr(reminder, '\n');
      return ptr;
 }
  return NULL;
}

// 自定义ft_strjoin函数
char    *ft_strjoin(char  *s1, char  *s2)
{
    size_t  size_s1;
    size_t  size_s2;
    char    *strjoin;

    size_s1 = ft_strlen(s1);
    size_s2 = ft_strlen(s2);
    if (!(strjoin = malloc(size_s1 + size_s2 + 1)))
        return (NULL);
    ft_strcpy(strjoin, s1);
    ft_strcat(strjoin, s2);
    if(!(ft_strcmp(s1, "") == 0))
    {
        free(s1);
     }
    return (strjoin);
}

内存泄漏原因分析

  1. 静态变量初始化错误:reminder初始化为字符串字面量"",后续被ft_strjoin分配的堆内存覆盖,但当reminder被替换为ft_strchr返回的内部指针时,原堆内存地址丢失,无法释放。
  2. reminder替换时未释放旧内存:执行reminder = ft_strchr(reminder, '\n');时,ft_strchr返回的是原reminder字符串内部的指针,原reminder指向的堆内存没有被free,直接丢失导致泄漏。
  3. 冗余内存未释放:初始malloc的line变量后续被ft_substr的返回值覆盖,原line的内存未释放,造成泄漏。
  4. 空字符串处理逻辑缺陷:ft_strjoin中对空字符串s1的判断依赖ft_strcmp,当s1为NULL时会直接崩溃,且初始字面量""不能被free,逻辑存在风险。

修复方案与修改后代码

核心修复点

  • 将静态变量reminder初始化为NULL,统一用堆内存管理
  • 替换reminder前先保存旧指针,释放旧内存后再赋值新内存
  • 修正ft_strjoin对NULL输入的处理逻辑
  • 覆盖指针前先释放原内存,避免冗余泄漏

修改后代码

#include "get_next_line.h"

char *get_next_line(int fd) {
    static char *reminder = NULL;
    char buf[BUFFER_SIZE + 1];
    int nbytes;
    char *line = NULL;

    // 文件描述符合法性检查
    if (fd < 0 || fd > 999 || BUFFER_SIZE <= 0) {
        return NULL;
    }

    // 循环读取文件内容,直到遇到换行或EOF
    while ((nbytes = read(fd, buf, BUFFER_SIZE)) > 0) {
        buf[nbytes] = '\0';
        // 将新读取的内容与reminder拼接
        reminder = ft_strjoin(reminder, buf);
        if (!reminder) {
            return NULL;
        }

        // 检查是否存在换行符
        char *newline_pos = ft_strchr(reminder, '\n');
        if (newline_pos) {
            // 截取换行前的内容作为返回行(包含换行符)
            line = ft_substr(reminder, 0, newline_pos - reminder + 1);
            if (!line) {
                free(reminder);
                reminder = NULL;
                return NULL;
            }

            // 保存换行后的剩余内容,分配新内存存储
            char *new_reminder = ft_substr(newline_pos + 1, 0, ft_strlen(newline_pos + 1));
            free(reminder);
            reminder = new_reminder;
            return line;
        }
    }

    // 处理读取错误的情况
    if (nbytes < 0) {
        free(reminder);
        reminder = NULL;
        return NULL;
    }

    // 处理剩余无换行的内容
    if (reminder && ft_strlen(reminder) > 0) {
        line = ft_substr(reminder, 0, ft_strlen(reminder));
        free(reminder);
        reminder = NULL;
        return line;
    }

    // 无内容可返回,清理静态内存
    free(reminder);
    reminder = NULL;
    return NULL;
}

// 修正后的ft_strjoin
char    *ft_strjoin(char  *s1, char  *s2)
{
    size_t  size_s1 = 0;
    size_t  size_s2 = 0;
    char    *strjoin;

    if (s1) {
        size_s1 = ft_strlen(s1);
    }
    if (s2) {
        size_s2 = ft_strlen(s2);
    }

    strjoin = malloc(size_s1 + size_s2 + 1);
    if (!strjoin) {
        if (s1) free(s1);
        return NULL;
    }

    // 拷贝s1内容,处理s1为NULL的情况
    if (s1) {
        ft_strcpy(strjoin, s1);
    } else {
        strjoin[0] = '\0';
    }
    // 拼接s2内容,处理s2为NULL的情况
    if (s2) {
        ft_strcat(strjoin, s2);
    }

    // 释放原s1的内存
    if (s1) {
        free(s1);
    }
    return strjoin;
}

// 补充需要的自定义函数示例(需自行实现)
size_t  ft_strlen(const char *s) {
    size_t len = 0;
    while (s && s[len]) len++;
    return len;
}

void    ft_strcpy(char *dest, const char *src) {
    while (src && *src) {
        *dest++ = *src++;
    }
    *dest = '\0';
}

void    ft_strcat(char *dest, const char *src) {
    while (dest && *dest) dest++;
    while (src && *src) {
        *dest++ = *src++;
    }
    *dest = '\0';
}

char    *ft_strchr(const char *s, int c) {
    if (!s) return NULL;
    while (*s) {
        if (*s == (char)c) {
            return (char *)s;
        }
        s++;
    }
    return NULL;
}

char    *ft_substr(const char *s, unsigned int start, size_t len) {
    if (!s) return NULL;
    size_t s_len = ft_strlen(s);
    if (start >= s_len) {
        char *empty = malloc(1);
        if (empty) *empty = '\0';
        return empty;
    }
    if (len > s_len - start) {
        len = s_len - start;
    }
    char *sub = malloc(len + 1);
    if (!sub) return NULL;
    ft_strcpy(sub, s + start);
    sub[len] = '\0';
    return sub;
}

修复说明

  1. 静态变量管理:reminder初始化为NULL,所有内容都用堆内存存储,替换前必释放旧内存,避免泄漏。
  2. 换行后剩余内容处理:不再直接使用ft_strchr返回的内部指针,而是用ft_substr分配新内存存储剩余内容,确保原reminder内存能被释放。
  3. ft_strjoin鲁棒性提升:支持s1为NULL的情况,统一处理空输入,避免崩溃和错误free。
  4. 冗余内存清理:移除初始不必要的line分配,所有返回的内存都由明确的malloc和free配对管理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 03:40:48