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

自定义实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 12:07:33