I am writing a function in C to load a file as a double dimensional array (char **
), the problem is that according to Valgrind I have memory leaks, can you help me?
I provide you with a complete example that can be reproduced. Also, my school only allows me certain functions to perform this task: open
, fopen
, close
, fclose
, malloc
, free
, getline
, lseek
... (I am not allowed to use stat
).
I can use my own implementation of realloc
too.
My code:
#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;
}
My input test file :
|A B C D E F G H
- ---------------
1|. . . . . . . .
2|. . . . . . . .
3|. . . . . . . .
4|. . . . . . . .
5|. . . . . . . .
6|. . . . . . . .
7|. . . . . . . .
8|. . . . . . . .
Valgrind's report:
❯ 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)
CodePudding user response:
Here are some problems:
you should reset
line_buff_size
to0
afterline_buff = NULL;
infill_from_file
. Or better: keep theline_buff
buffer growing as you iterate in the loop and only free it after the end of the loop.you should pass the current array length to
append_word_array
to avoid scanning for the NULL terminator.testing
if (line[len - 1] == '\n')
has undefined behavior ifline
has a length of0
, which is possible if the file contains embedded null bytes.in case of allocation error for the initial call to
malloc()
, you returnNULL
omitting thefclose(file)
.if
realloc
is allowed, you could use it with caution inappend_word_array
.
These issues are unlikely to have caused the reported memory leaks. There is a chance the problem is not related to your code (or to this version of the code). You should try and reduce the test file length and post the smallest input that still produces a leak summary with valgrind?
You can also try this modified version:
#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;
}