get_next_line函数实现存在内存泄漏,寻求修复方案
无法修复get_next_line的内存泄漏问题
我实现的get_next_line是一个学生项目,用于学习静态变量,功能是通过文件描述符逐行读取文件。函数逻辑在空文件、单字符文件、单行文件等场景下均正常,但内存泄漏问题始终存在。使用Valgrind检测(执行./a.out 5读取含4行文本的文件),结果显示程序退出时存在76字节的确定丢失内存,涉及malloc、strdup、ft_strjoin、ft_strcut等操作的内存分配。
我怀疑泄漏来自静态变量stash,但提前释放会导致无法读取剩余行;仅在返回最后一行时释放stash,且已确保释放了read相关的buffer,但读完最后一行后仍存在泄漏,对此感到困惑。
项目代码
gnl_utils.c
#include "get_next_line.h" size_t ft_strlen(const char *str) { size_t i; i = 0; while (str[i]) i++; return (i); } char *ft_strjoin(const char *s1, const char *s2) { char *str; size_t size; int i; int j; size = ft_strlen(s1) + ft_strlen(s2); str = malloc(size * sizeof(char) + 1); if (!str) return (NULL); i = 0; j = 0; while (s1[i]) str[j++] = s1[i++]; i = 0; while (s2[i]) str[j++] = s2[i++]; str[j] = '\0'; return (str); } char *ft_strchr(const char *s, int c) { while (*s != '\0') { if ((unsigned char)*s == (unsigned char)c) return ((char *)s); s++; } if (c == 0) return ((char *)s); return (0); } char *ft_substr(const char *s, unsigned int start, size_t len) { char *str; int i; int y; if (start >= ft_strlen(s)) start = ft_strlen(s); y = 0; while (start + y < start + len && s[start + y] != '\0') y++; i = 0; str = malloc((y) + 1); if (!str || start > ft_strlen(s)) return (NULL); while (i < y) { str[i] = s[start]; i++; start++; } str[i] = '\0'; return (str); } char *ft_strdup(const char *s) { char *dup; int i; i = 0; dup = malloc(ft_strlen(s) + 1); if (!dup) return (NULL); while (s[i]) { dup[i] = s[i]; i++; } dup[i] = '\0'; return (dup); }
gnl.c
#include "get_next_line.h" static int check_line(char *str) { int i; i = 0; if (!str) return (-1); while (str[i]) { if (str[i] == '\n') return (1); i++; } return (-1); } static char *ft_strcut(char *str) { char *cutted_str; int i; int j; i = 0; j = 0; while (str[i] != '\n') i++; cutted_str = malloc(sizeof(char) * (ft_strlen(str) - i + 1)); if (!cutted_str) return (NULL); i++; while (str[i]) cutted_str[j++] = str[i++]; cutted_str[j] = '\0'; free(str); return (cutted_str); } char *get_next_line(int fd) { static char *stash; char *buff; char *line; int readed; if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); buff = malloc(sizeof(char) * (BUFFER_SIZE + 1)); if (!buff) return (NULL); readed = read(fd, buff, BUFFER_SIZE); if (readed <= 0 && !stash) return (NULL); buff[readed] = '\0'; if (!stash) stash = strdup(buff); else stash = ft_strjoin(stash, buff); free(buff); while (stash) { buff = malloc(sizeof(char) * (BUFFER_SIZE + 1)); readed = read(fd, buff, BUFFER_SIZE); buff[readed] = '\0'; stash = ft_strjoin(stash, buff); free(buff); if (readed < BUFFER_SIZE && check_line(stash) == -1) { line = ft_strdup(stash); stash = NULL; free(stash); return (line); } else if (check_line(stash) != -1) { line = ft_substr(stash, 0, ft_strchr(stash, '\n') - stash + 1); stash = ft_strcut(stash); return (line); } } return (NULL); } int main(int argc, char **argv) { char *line; int i = 0; int fd = open("text.txt", O_RDONLY); if (argc == 2) { while (i < atoi(argv[1])) { line = get_next_line(fd); printf("Ligne %d : %s\n", i + 1, line); free(line); i++; } } return (0); }
核心泄漏点与修复方案
1. 关键泄漏原因
- stash释放逻辑错误:最后一行处理时
stash = NULL; free(stash);完全无效,先将stash置为NULL再释放等于没操作,原stash内存泄漏。 - ft_strjoin内存泄漏:每次拼接
stash = ft_strjoin(stash, buff);时,旧stash指针被覆盖,未释放原内存。 - 未处理残留stash:若程序未读完所有行(如测试用例读5次但文件仅4行),stash中剩余数据未释放。
- 初始read后的buff泄漏:当
readed <=0 && !stash时,直接返回NULL未释放buff。
2. 修复后的get_next_line函数
char *get_next_line(int fd) { static char *stash; char *buff; char *line; char *temp; // 保存旧stash地址,避免泄漏 int readed; if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); buff = malloc(sizeof(char) * (BUFFER_SIZE + 1)); if (!buff) return (NULL); readed = read(fd, buff, BUFFER_SIZE); if (readed <= 0 && !stash) { free(buff); // 先释放buff再返回 return (NULL); } buff[readed] = '\0'; if (!stash) stash = ft_strdup(buff); else { temp = stash; stash = ft_strjoin(stash, buff); free(temp); // 释放旧stash } free(buff); while (stash) { buff = malloc(sizeof(char) * (BUFFER_SIZE + 1)); if (!buff) { free(stash); // 分配失败时清理stash stash = NULL; return (NULL); } readed = read(fd, buff, BUFFER_SIZE); buff[readed] = '\0'; temp = stash; stash = ft_strjoin(stash, buff); free(temp); free(buff); // 处理最后一行 if (readed < BUFFER_SIZE && check_line(stash) == -1) { line = ft_strdup(stash); free(stash); // 直接释放原stash stash = NULL; return (line); } // 处理带换行的行 else if (check_line(stash) != -1) { line = ft_substr(stash, 0, ft_strchr(stash, '\n') - stash + 1); stash = ft_strcut(stash); // ft_strcut已内部释放旧stash return (line); } } return (NULL); }
3. 补充:手动清理stash的接口
由于静态变量stash生命周期为整个程序,若需在读完文件后彻底清理,可新增辅助函数:
void gnl_clean(void) { free(stash); stash = NULL; }
在main函数循环结束后调用:
// main函数末尾 gnl_clean(); close(fd);
内容的提问来源于stack exchange,提问作者nokosse
相关产品推荐
相关产品推荐

