自定义实现get_next_line函数陷入死循环,求排查解决
解决get_next_line死循环问题及代码优化建议
我正在实现一个从文件描述符按BUFFER_SIZE分块读取内容直到找到'\n'或EOF的程序,但目前陷入死循环。作业仅允许使用WRITE、READ、MALLOC、FREE标准函数,因此自行实现了ft_realloc、ft_strlen等函数(假设这些实现正确)。以下是代码及问题排查需求:
void *ft_realloc(void *ptr, size_t oldsize, size_t size) { void *new; size_t minsize; if (size == 0) { free(ptr); return (NULL); } if (ptr == NULL) return (malloc(size)); new = malloc(size); if (!new) return (NULL); minsize = size; if (oldsize < size) minsize = oldsize; memcpy(new, ptr, minsize); while (size - oldsize != 0) ((char *)new)[oldsize++] = '\0'; free(ptr); return (new); } int find_newline_eof(char *buffer) { int i; i = 0; if (!buffer) return (-1); while (buffer[i]) { if (buffer[i] == '\n') return (i); i++; } return (-1); } char *extract_line(char **buffer, int newline_pos) { char *line; int remaining_len; //参数说明:若newline_pos为-1,表示已到达EOF且未找到'\n' if (!*buffer) return (NULL); if (newline_pos == -1) newline_pos = ft_strlen(*buffer); line = malloc(newline_pos + 1); if (!line) return (NULL); memcpy(line, *buffer, newline_pos); line[newline_pos] = '\0'; if ((*buffer)[newline_pos] == '\n') newline_pos++; printf("reached EXTRACT_LINE, line: (%s)\n", line); return (NULL); // 函数执行到此处,错误出在后续代码 remaining_len = ft_strlen(*buffer + newline_pos); // memmove比memcpy更安全,可处理内存重叠情况 memmove(*buffer, *buffer + newline_pos, remaining_len + 1); *buffer = ft_realloc(*buffer, ft_strlen(*buffer), remaining_len + 1); if (!*buffer) { free(line); return (NULL); } return (line); } int read_newpiece(int fd, char **buffer) { int bytes_readed; char current_buffer[BUFFER_SIZE + 1]; char *new_buffer; bytes_readed = read(fd, current_buffer, BUFFER_SIZE); if (bytes_readed <=0) //安全检查 return (bytes_readed); current_buffer[bytes_readed] = '\0'; //printf("entered here!\n"); //return (bytes_readed); if (!*buffer) { *buffer = malloc(1); if (!*buffer) return (-1); (*buffer)[0] = '\0'; // 初始化为空字符串 } new_buffer = ft_realloc(*buffer, ft_strlen(*buffer), ft_strlen(*buffer) + bytes_readed + 1); if (!new_buffer) return (-1); *buffer = new_buffer; strncat(*buffer, current_buffer, bytes_readed); return (bytes_readed); } char *get_next_line(int fd) { static char *buffer = NULL; char *line; //返回值 int newline_index; //找到EOF或'\n'的位置 if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); if (!buffer) { buffer = malloc(1); if (!buffer) return (NULL); buffer[0] = '\0'; } newline_index = -1; //-1表示未找到 while (newline_index == -1 && read_newpiece(fd, &buffer) > 0) { printf("buffer after read: /%s/\n", buffer); newline_index = find_newline_eof(buffer); printf("newline index: %d\n", newline_index); //return (NULL); } if (newline_index == -1 && !buffer) return (NULL); line = extract_line(&buffer, newline_index); printf("extracted line: ///%s///\n", line); return (NULL); } int main(int ac, char **av) { int fd; char *line; if (ac < 2) return (write(1, "Usage: ./a.out <filename>\n", 27)); fd = open(av[1], O_RDONLY); if (fd <= -1) //安全检查 return (write(1, "error opening file\n", 20)); while ((line = get_next_line(fd)) != NULL) { printf("%s\n", line); free (line); } close (fd); return (0); }
导致死循环的核心原因
get_next_line强制返回NULL:函数末尾的return (NULL);会让main的循环条件while ((line = get_next_line(fd)) != NULL)永远不成立,若你注释掉该语句后出现死循环,问题则出在buffer处理逻辑。extract_line的测试返回语句:中间添加的return (NULL);会跳过后续的buffer截断、内存重分配代码,导致已处理内容永久残留在buffer中。下次调用get_next_line时会重复识别同一个换行符,陷入无效的重复调用流程。
代码修复及优化建议
1. 移除测试用返回语句,恢复核心逻辑
删除extract_line中间的return (NULL);,确保buffer截断和重分配代码正常执行;同时删除get_next_line末尾的强制return (NULL);,改为返回处理后的line。
2. 替换非允许的标准函数
作业仅允许使用指定函数,需替换memcpy、memmove、strncat为自定义实现:
// 自定义字节拷贝 void ft_memcpy(void *dst, const void *src, size_t n) { char *d = (char *)dst; const char *s = (const char *)src; while (n--) *d++ = *s++; } // 自定义内存移动(处理重叠) void ft_memmove(void *dst, const void *src, size_t n) { char *d = (char *)dst; const char *s = (const char *)src; if (d < s) while (n--) *d++ = *s++; else while (n--) *(d + n) = *(s + n); } // 自定义字符串拼接 void ft_strcat(char *dst, const char *src) { while (*dst) dst++; while (*src) *dst++ = *src++; *dst = '\0'; }
3. 修正ft_realloc的填充逻辑
将原while循环填充0改为更高效的批量填充:
void *ft_realloc(void *ptr, size_t oldsize, size_t size) { void *new; size_t minsize; if (size == 0) { free(ptr); return (NULL); } if (ptr == NULL) return (malloc(size)); new = malloc(size); if (!new) return (NULL); minsize = (oldsize < size) ? oldsize : size; ft_memcpy(new, ptr, minsize); if (size > oldsize) ft_memset((char *)new + oldsize, '\0', size - oldsize); free(ptr); return (new); } // 自定义memset实现 void ft_memset(void *b, int c, size_t len) { char *p = (char *)b; while (len--) *p++ = (char)c; }
4. 优化get_next_line的循环逻辑
先检查已有buffer中是否存在换行,避免不必要的read调用;同时处理EOF后buffer为空的情况:
char *get_next_line(int fd) { static char *buffer = NULL; char *line; int newline_index; if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); if (!buffer) { buffer = malloc(1); if (!buffer) return (NULL); buffer[0] = '\0'; } newline_index = find_newline_eof(buffer); while (newline_index == -1 && read_newpiece(fd, &buffer) > 0) { newline_index = find_newline_eof(buffer); } if (newline_index == -1 && ft_strlen(buffer) == 0) { free(buffer); buffer = NULL; return (NULL); } line = extract_line(&buffer, newline_index); return (line); }
5. 修正extract_line的ft_realloc参数传递
原代码中ft_realloc的oldsize参数传递错误,需改为原buffer的实际容量(或调整逻辑,直接基于剩余长度重分配):
char *extract_line(char **buffer, int newline_pos) { char *line; int remaining_len; size_t original_len; if (!*buffer) return (NULL); original_len = ft_strlen(*buffer); if (newline_pos == -1) newline_pos = original_len; line = malloc(newline_pos + 1); if (!line) return (NULL); ft_memcpy(line, *buffer, newline_pos); line[newline_pos] = '\0'; if ((*buffer)[newline_pos] == '\n') newline_pos++; remaining_len = ft_strlen(*buffer + newline_pos); ft_memmove(*buffer, *buffer + newline_pos, remaining_len + 1); *buffer = ft_realloc(*buffer, original_len, remaining_len + 1); if (!*buffer) { free(line); return (NULL); } return (line); }
内容的提问来源于stack exchange,提问作者Rache Bartmoss
相关产品推荐
相关产品推荐

