在 C 中读取文件并将其转换为二维数组时发生内存泄漏

Memory leaks when reading and transforming a file into a two-dimensional array in C

提问人:Virgil G. 提问时间:1/30/2023 最后编辑:Virgil G. 更新时间:1/30/2023 访问量:173

问:

我正在用 C 编写一个函数来将文件加载为二维数组 (),问题是根据 Valgrind 我有内存泄漏,你能帮我吗?char **

我为您提供了一个可以复制的完整示例。此外,我的学校只允许我执行某些功能来执行此任务:、、、、......(我不被允许使用)。openfopenclosefclosemallocfreegetlinelseekstat

我也可以使用我自己的实现。realloc

我的代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void my_free_word_array(char **word_array)
{
    size_t i = 0;
    if (!word_array) {
        return;
    }
    while (word_array[i] != NULL) {
        free(word_array[i]);
        ++i;
    }
    free(word_array);
}

char **append_word_array(char **array, char *line)
{
    size_t array_len = 0;
    while (array[array_len])
        array_len++;
    size_t len = strlen(line);
    if (line[len - 1] == '\n')
        line[len - 1] = '\0';
    char **new_array = malloc(sizeof(char *) * (array_len + 2));
    for (size_t i = 0; i < array_len; i++)
        new_array[i] = array[i];
    new_array[array_len] = strdup(line);
    new_array[array_len + 1] = NULL;
    free(array);
    return new_array;
}

char **fill_from_file(char **array, FILE *file)
{
    char *line_buff = NULL;
    size_t line_buff_size = 0;
    ssize_t line_size = getline(&line_buff, &line_buff_size, file);
    while (line_size >= 0) {
        array = append_word_array(array, line_buff);
        free(line_buff);
        line_buff = NULL;
        line_size = getline(&line_buff, &line_buff_size, file);
    }
    free(line_buff);
    return array;
}

char **my_load_file_to_line_array(const char *filepath)
{
    char **word_array = NULL;
    FILE *file = fopen(filepath, "r");
    if (!file)
        return NULL;
    word_array = malloc(sizeof(char *));
    if (!word_array)
        return NULL;
    word_array[0] = NULL;
    word_array = fill_from_file(word_array, file);
    fclose(file);
    return word_array;
}

int main(int argc, char **argv)
{
    if (argc < 2)
        return -1; 
    char **array = my_load_file_to_line_array(argv[1]);
    my_free_word_array(array);
    return 0;
}

我的输入测试文件:

 |A B C D E F G H
-+---------------
1|. . . . . . . .
2|. . . . . . . .
3|. . . . . . . .
4|. . . . . . . .
5|. . . . . . . .
6|. . . . . . . .
7|. . . . . . . .
8|. . . . . . . .

Valgrind的报告:

❯ valgrind --leak-check=full ./navy coord.txt
==99157== Memcheck, a memory error detector
==99157== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==99157== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==99157== Command: ./navy coord.txt
==99157== 
==99157== 
==99157== HEAP SUMMARY:
==99157==     in use at exit: 72 bytes in 5 blocks
==99157==   total heap usage: 84 allocs, 79 frees, 18,512 bytes allocated
==99157== 
==99157== 72 (40 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==99157==    at 0x4842888: malloc (vg_replace_malloc.c:381)
==99157==    by 0x10980F: append_word_array (my_load_file_to_line_array.c:18)
==99157==    by 0x109914: fill_from_file (my_load_file_to_line_array.c:33)
==99157==    by 0x1099F0: my_load_file_to_line_array (my_load_file_to_line_array.c:62)
==99157==    by 0x1092A5: main (navy.c:17)
==99157== 
==99157== LEAK SUMMARY:
==99157==    definitely lost: 40 bytes in 1 blocks
==99157==    indirectly lost: 32 bytes in 4 blocks
==99157==      possibly lost: 0 bytes in 0 blocks
==99157==    still reachable: 0 bytes in 0 blocks
==99157==         suppressed: 0 bytes in 0 blocks
==99157== 
==99157== For lists of detected and suppressed errors, rerun with: -s
==99157== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
c malloc fopen glibc

评论

2赞 Support Ukraine 1/30/2023
“我给你一个完整的例子,可以复制。” 嗯......?main
2赞 Harith 1/30/2023
“我给你提供了一个可以复制的完整例子。”---> 无法重现。
0赞 Harith 1/30/2023
中有一个错别字。main()lod... ---> load...
1赞 Craig Estey 1/30/2023
我使用一些简单的输入下载并运行了您的程序。它报告说没有泄漏。您使用的导致问题的 [最小] 输入是什么?请编辑您的问题,并将示例输入发布在单独的代码块中。valgrind
2赞 KamilCuk 1/30/2023
我还编译并运行了您的程序。没有泄漏。可能是您正在测试旧版本。

答:

0赞 chqrlie 1/30/2023 #1

以下是一些问题:

  • 您应该在 中重置为 after 。或者更好的是:在循环中迭代时保持缓冲区增长,并且仅在循环结束后释放它。line_buff_size0line_buff = NULL;fill_from_fileline_buff

  • 应传递当前数组长度,以避免扫描 NULL 终止符。append_word_array

  • 如果长度为 ,则测试具有未定义的行为,如果文件包含嵌入的 null 字节,则这是可能的。if (line[len - 1] == '\n')line0

  • 如果初始调用 时出现分配错误,则返回省略 .malloc()NULLfclose(file)

  • 如果允许,您可以在 中谨慎使用它。reallocappend_word_array

这些问题不太可能导致报告的内存泄漏。问题可能与您的代码(或此版本的代码)无关。您应该尝试减少测试文件的长度,并发布最小的输入,该输入仍会生成 valgrind 的泄漏摘要?

您也可以尝试此修改版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void my_free_word_array(char **word_array)
{
    if (word_array) {
        size_t i = 0;
        while (word_array[i] != NULL) {
            free(word_array[i]);
            ++i;
        }
        free(word_array);
    }
}

char **append_word_array(char **array, size_t *array_len, const char *line)
{
    char *copy = strdup(line);
    char **new_array = realloc(array, sizeof(*array) * (*array_len + 2));
    if (copy == NULL || new_array == NULL) {
        fprintf(stderr, "memory allocation error");
        exit(1);
    }
    new_array[*array_len] = copy;
    new_array[*array_len + 1] = NULL;
    *array_len += 1;
    return new_array;
}

char **fill_from_file(FILE *file)
{
    char **array = NULL;
    size_t array_len = 0;
    char *line_buff = NULL;
    size_t line_buff_size = 0;
    ssize_t line_length;
    while ((line_size = getline(&line_buff, &line_buff_size, file)) >= 0) {
        if (line_length > 0 && line_buff[line_length - 1] == '\n')
            line_buff[line_length - 1] = '\0';
        array = append_word_array(array, &array_len, line_buff);
    }
    free(line_buff);
    return array;
}

char **my_load_file_to_line_array(const char *filepath)
{
    FILE *file = fopen(filepath, "r");
    if (!file)
        return NULL;
    char **word_array = fill_from_file(file);
    fclose(file);
    return word_array;
}

int main(int argc, char **argv)
{
    if (argc < 2)
        return -1; 
    char **array = my_load_file_to_line_array(argv[1]);
    my_free_word_array(array);
    return 0;
}