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

自定义get_next_line实现出现无效指针错误的排查求助

get_next_line 内存释放错误排查与修复

问题现象

实现的get_next_line函数存在以下异常:

  • 读取文件最后一行时,line变量持有该行内容,但在main函数循环内执行free(line)会触发无效指针错误;
  • 将free移到循环外时读取正常,但会引发内存泄漏;
  • 仅当BUFFER_SIZE设为1或10000时程序运行正常,设为10或100时出错,无法确定是main结构问题还是函数本身逻辑缺陷。

相关代码

main函数

int    main(void)
{
    int fd;
    char *line;

    fd = open("file.txt", O_RDONLY);
    while ((line = get_next_line(fd)) != NULL)
    {
        printf("%s", line);
        free(line);
    }
    printf("%s", line);
    free(line);
    return(0);
}

get_next_line核心实现

#include "get_next_line.h"

char    *cut_line(char  *line, char *new_line)
{
    int i;

    i = 0;
    while (line[i] != '\n' && line[i] != '\0')
        i++;
    if (line[i] == '\n')
        i++;
    while (line[i])
    {
        *new_line = line[i];
        line[i] = '\0';
        new_line++;
        i++;
    }
    while (*new_line)
    {
        *new_line = '\0';
        new_line++;
    }
    return (line);
}

char    *finalize_line(char **line, char **new_line)
{
    *line = cut_line(*line, *new_line);
    if (**new_line == '\0')
    {
        free(*new_line);
        *new_line = NULL;
    }
    if (**line == '\0' && !*new_line)
    {
        free(*line);
        *line = NULL;
        return (NULL);
    }
    return (*line);
}

char    *get_next_line(int fd)
{
    static char *new_line;
    char        *buffer;
    char        *line;
    int         read_bytes;

    buffer = ft_calloc(BUFFER_SIZE + 1, sizeof(char));
    if (!new_line)
        new_line = ft_calloc(BUFFER_SIZE + 1, sizeof(char));
    if (fd < 0 || BUFFER_SIZE <= 0 || !buffer || !new_line)
        return (free_null(&buffer, &new_line));
    line = ft_strjoin(new_line, buffer, 0);
    read_bytes = 1;
    while (!(ft_strchr(buffer, '\n')) && read_bytes > 0)
    {
        read_bytes = read(fd, buffer, BUFFER_SIZE);
        if (read_bytes == 0)
            break ;
        if (read_bytes == -1)
            return (free_null(&buffer, &new_line));
        buffer[read_bytes] = '\0';
        line = ft_strjoin(line, buffer, 1);
    }
    free(buffer);
    return (finalize_line(&line, &new_line));
}

ft_strjoin实现

char    *ft_strjoin(char *s1, char *s2, int flag)
{
    char    *aux_s1;
    int     i;
    int     j;

    if (!s1)
        s1 = ft_calloc(1, sizeof(char));
    if (!s2)
        s2 = ft_calloc(1, sizeof(char));
    aux_s1 = (char *)malloc(ft_strlen(s1) + ft_strlen(s2) + 1);
    if (!aux_s1)
        return (0);
    i = 0;
    while (s1[i] != '\0')
    {
        aux_s1[i] = s1[i];
        i++;
    }
    j = 0;
    while (s2[j] != '\0')
        aux_s1[i++] = s2[j++];
    aux_s1[i] = '\0';
    if (flag == 1)
        free(s1);
    return (aux_s1);
}

问题根源分析

  1. main函数的非法操作
    循环结束后line已经是NULL,但代码仍执行printf("%s", line);和free(line);,这会导致空指针解引用和无效释放,是直接触发错误的原因。

  2. cut_line函数的内存越界
    new_line初始大小固定为BUFFER_SIZE+1,当line中剩余字符长度超过该值时,会向new_line内存范围外写入数据,破坏堆结构,后续free操作就会触发无效指针错误。这也是不同BUFFER_SIZE表现不同的原因——当BUFFER_SIZE足够大时,剩余字符不会超出其长度,越界不会发生;反之则会破坏内存。

  3. finalize_line函数的逻辑漏洞
    当**line == '\0'且!*new_line时,代码释放*line并置为NULL,返回NULL后main循环结束,后续又对NULL执行free,引发错误。

修复方案

1. 修正main函数

移除循环外对line的非法操作,同时补充关闭文件描述符:

int    main(void)
{
    int fd;
    char *line;

    fd = open("file.txt", O_RDONLY);
    while ((line = get_next_line(fd)) != NULL)
    {
        printf("%s", line);
        free(line);
    }
    close(fd);
    return(0);
}

2. 修复cut_line的内存越界问题

改为动态分配new_line的内存,确保能容纳所有剩余字符:

char    *cut_line(char  *line, char **new_line)
{
    int i;
    int rest_len;

    i = 0;
    while (line[i] != '\n' && line[i] != '\0')
        i++;
    if (line[i] == '\n')
        i++;
    
    // 计算剩余字符长度
    rest_len = 0;
    while (line[i + rest_len] != '\0')
        rest_len++;
    
    // 释放旧空间,重新分配足够内存
    free(*new_line);
    *new_line = ft_calloc(rest_len + 1, sizeof(char));
    if (!*new_line)
    {
        free(line);
        return NULL;
    }
    
    // 复制剩余字符
    rest_len = 0;
    while (line[i])
    {
        (*new_line)[rest_len] = line[i];
        line[i] = '\0';
        rest_len++;
        i++;
    }
    return (line);
}

3. 调整finalize_line函数

适配cut_line的参数修改,完善逻辑:

char    *finalize_line(char **line, char **new_line)
{
    *line = cut_line(*line, new_line);
    if (!*line)
        return NULL;
    
    if (**new_line == '\0')
    {
        free(*new_line);
        *new_line = NULL;
    }
    if (**line == '\0' && !*new_line)
    {
        free(*line);
        *line = NULL;
        return (NULL);
    }
    return (*line);
}

4. 修正get_next_line初始逻辑

调整new_line的初始分配方式,修正循环条件的换行符检查:

char    *get_next_line(int fd)
{
    static char *new_line;
    char        *buffer;
    char        *line;
    int         read_bytes;

    buffer = ft_calloc(BUFFER_SIZE + 1, sizeof(char));
    if (!buffer)
        return (free_null(&buffer, &new_line));
    if (!new_line)
        new_line = ft_calloc(1, sizeof(char));
    if (fd < 0 || BUFFER_SIZE <= 0 || !new_line)
        return (free_null(&buffer, &new_line));
    
    line = ft_strjoin(new_line, "", 0);
    free(new_line);
    new_line = NULL;
    read_bytes = 1;
    
    // 检查当前line中是否有换行符,无则继续读取
    while (!(ft_strchr(line, '\n')) && read_bytes > 0)
    {
        read_bytes = read(fd, buffer, BUFFER_SIZE);
        if (read_bytes == 0)
            break ;
        if (read_bytes == -1)
        {
            free(line);
            return (free_null(&buffer, &new_line));
        }
        buffer[read_bytes] = '\0';
        line = ft_strjoin(line, buffer, 1);
        if (!line)
            return (free_null(&buffer, &new_line));
    }
    free(buffer);
    return (finalize_line(&line, &new_line));
}

5. 补充free_null函数实现

确保能正确释放指针并置空:

void    *free_null(char **ptr1, char **ptr2)
{
    if (ptr1 && *ptr1)
    {
        free(*ptr1);
        *ptr1 = NULL;
    }
    if (ptr2 && *ptr2)
    {
        free(*ptr2);
        *ptr2 = NULL;
    }
    return NULL;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 09:00:10