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); }
内存泄漏原因分析
- 静态变量初始化错误:
reminder初始化为字符串字面量"",后续被ft_strjoin分配的堆内存覆盖,但当reminder被替换为ft_strchr返回的内部指针时,原堆内存地址丢失,无法释放。 reminder替换时未释放旧内存:执行reminder = ft_strchr(reminder, '\n');时,ft_strchr返回的是原reminder字符串内部的指针,原reminder指向的堆内存没有被free,直接丢失导致泄漏。- 冗余内存未释放:初始
malloc的line变量后续被ft_substr的返回值覆盖,原line的内存未释放,造成泄漏。 - 空字符串处理逻辑缺陷:
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; }
修复说明
- 静态变量管理:
reminder初始化为NULL,所有内容都用堆内存存储,替换前必释放旧内存,避免泄漏。 - 换行后剩余内容处理:不再直接使用
ft_strchr返回的内部指针,而是用ft_substr分配新内存存储剩余内容,确保原reminder内存能被释放。 ft_strjoin鲁棒性提升:支持s1为NULL的情况,统一处理空输入,避免崩溃和错误free。- 冗余内存清理:移除初始不必要的
line分配,所有返回的内存都由明确的malloc和free配对管理。
内容的提问来源于stack exchange,提问作者42nenuser
相关产品推荐
相关产品推荐

