Can't detect a memory leak

100 Views Asked by At

I wrote a program to search for the longest sentence in several text files:

/* lngst_sentence_file_competition.c */

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

enum consts {
    str_len = 5
};

typedef struct tag_item {
    int sentence_length;
    char *sentence;
    struct tag_item *next;
} item;

void free_str_if_it_is_not_lngst(
    char **str, char **lngst_file_str, int *curr_sentence_len,
    int *file_lngst_sentence_len
)
{
    if (*curr_sentence_len <= *file_lngst_sentence_len)
        free(*str);
    else {
        if (*lngst_file_str) {
            free(*lngst_file_str);
            *lngst_file_str = NULL;
        }
        *lngst_file_str = *str;
        *file_lngst_sentence_len = *curr_sentence_len;
    }
    *curr_sentence_len = 0;
    *str = NULL;
}

void find_lngst_sentence_in_next_file(
    FILE *file, char **lngst_file_str, int *lngst_sentence_len
)
{
    char *str = malloc(str_len*sizeof(char));
    int str_mem_size = str_len;
    int c;
    int curr_sentence_len = 0;
    int file_lngst_sentence_len = 0;
    while ((c=fgetc(file)) != EOF) {
        switch (c) {
            case '.':
                str[curr_sentence_len] = '.';
                str[curr_sentence_len + 1] = '\0';
                free_str_if_it_is_not_lngst(
                    &str, lngst_file_str, &curr_sentence_len,
                    &file_lngst_sentence_len
                );
                str = malloc(str_len*sizeof(char));
                str_mem_size = str_len;
                /* falls through */
            case '\n':
            case '\t':
                continue;
        }
        curr_sentence_len++;
        if (curr_sentence_len+1 == str_mem_size) {
            str = realloc(str, str_mem_size + str_len);
            str_mem_size += str_len;
        }
        str[curr_sentence_len - 1] = c;
    }
    free_str_if_it_is_not_lngst(
        &str, lngst_file_str, &curr_sentence_len, &file_lngst_sentence_len
    );
    if (*lngst_sentence_len < file_lngst_sentence_len)
        *lngst_sentence_len = file_lngst_sentence_len;
}

int main(int argc, char **argv)
{
    FILE *file;
    item *first = NULL;
    int lngst_sentence_len = 0;
    int i;
    for (i=1; i < argc; i++) {
        char *str = NULL;
        file = fopen(argv[i], "r");
        if (!file) {
            perror(argv[i]);
            exit(1);
        }
        find_lngst_sentence_in_next_file(file, &str, &lngst_sentence_len);
        printf("%s\nString length is %d.\n", str, lngst_sentence_len);
        /* add_new_element_to_linked_list(&first, str); */
    }
    /* print_sentences(first); */
    return 0;
}

I also wrote a small test file test.txt:

a.
ab.
abc.

I ran my program using Valgrind:

valgrind --tool=memcheck --leak-check=full -s ./lngst_sentence_file_competition test.txt

Valgrind produced the following output:

==359271== 
abc.
String length is 3.
==359271== 
==359271== HEAP SUMMARY:
==359271==     in use at exit: 477 bytes in 2 blocks
==359271==   total heap usage: 7 allocs, 5 frees, 5,612 bytes allocated
==359271== 
==359271== 5 bytes in 1 blocks are definitely lost in loss record 1 of 2
==359271==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==359271==    by 0x109389: find_lngst_sentence_in_next_file (lngst_sentence_file_competition.c:53)
==359271==    by 0x1094F4: main (lngst_sentence_file_competition.c:87)
==359271== 
==359271== LEAK SUMMARY:
==359271==    definitely lost: 5 bytes in 1 blocks
==359271==    indirectly lost: 0 bytes in 0 blocks
==359271==      possibly lost: 0 bytes in 0 blocks
==359271==    still reachable: 472 bytes in 1 blocks
==359271==         suppressed: 0 bytes in 0 blocks
==359271== Reachable blocks (those to which a pointer was found) are not shown.
==359271== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==359271== 
==359271== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Can anyone see the source of the memory leak?

In my opinion, I've correctly freed all the allocated memory.

1

There are 1 best solutions below

0
Luan Mateus On BEST ANSWER

In your total usage heap you have 7 allocs and 5 frees, the last two remaining are related to the file pointer file and the str pointer.

At the beginning of for loop you define a char pointer as null, after finishing for loop there will still be a pointer that is not being used, therefore, place a free(str) after printf(), this way with each new interaction you will allocate and deallocate this memory space. Furthermore, close the file pointer as this detaches the memory space used to read it.

int main(int argc, char **argv)
    {
        FILE *file;
        item *first = NULL;
        int lngst_sentence_len = 0;
        int i;
        for (i=1; i < argc; i++) {
            char *str = NULL;
            file = fopen(argv[i], "r");
            if (!file) {
                perror(argv[i]);
                exit(1);
            }
            find_lngst_sentence_in_next_file(file, &str, &lngst_sentence_len);
            printf("%s\nString length is %d.\n", str, lngst_sentence_len);
            /* add_new_element_to_linked_list(&first, str); */
            free(str);
            fclose(file);
        }
        /* print_sentences(first); */
        return 0;
    }