views:

146

answers:

7
+3  Q: 

cannot free memory

Hello,

gcc 4.4.4 c89

I have the following function but I cannot free the memory. The message I get in Valgrind is suspecting the getline function. However, I am free the file pointer at the end of the function. So it cannot be that.

I have a global array of pointers to char 'candidate_names'. However, I haven't allocated any memory for it.

Many thanks for any advice,

The message I get in valgrind is the following.

HEAP SUMMARY:
==4021==     in use at exit: 840 bytes in 7 blocks
==4021==   total heap usage: 22 allocs, 15 frees, 1,332 bytes allocated
==4021== 
==4021== Searching for pointers to 7 not-freed blocks
==4021== Checked 48,412 bytes
==4021== 
==4021== 840 bytes in 7 blocks are still reachable in loss record 1 of 1
==4021==    at 0x4005BDC: malloc (vg_replace_malloc.c:195)
==4021==    by 0xAAE38D: getdelim (iogetdelim.c:68)
==4021==    by 0xAAADD2: getline (getline.c:34)
==4021==    by 0x804892B: load_candidates (candidate.c:61)
==4021==    by 0x8048686: main (driver.c:24)

My source code:

#define NUMBER_OF_CANDIDATES 7
static char *candidate_names[NAME_SIZE] = {0};

int load_candidates()
{
    FILE *fp = NULL;
    size_t i = 0;
    ssize_t read = 0;
    size_t n = 0;
    char *found = NULL;

    fp = fopen("votes.txt", "r");

    /* open the votes file */
    if(fp == NULL) {
        fprintf(stderr, "Cannot open votes file [ %s ]\n", strerror(errno));
        return FALSE;
    }

    /* fill the structure with candidates */
    for(i = 0; i < NUMBER_OF_CANDIDATES; ) {
        read = getline(&candidate_names[i], &n ,fp);
        if(read == -1) {
            fprintf(stderr, "Cannot read candidate [ %d ] [ %s ]\n",
                    i, strerror(errno));
            candidate_names[i] = "Invalid candidate";
            i++;
            continue;
        }
        /* Just ignore the key work in the text file */
        if(strcmp("CANDIDATES\n", candidate_names[i]) != 0) {
            /* Find and remove the carriage return */
            found = strchr(candidate_names[i], '\n');
            if(found != NULL) {
                /* Remove the carriage return by nul terminating */
                *found = '\0';
            }
            i++;
        }
    }

    fclose(fp);

    return TRUE;
}

EDIT ========= FREEING candidate_names ======

All heap blocks were freed -- no leaks are possible
==4364== 
==4364== ERROR SUMMARY: 84 errors from 2 contexts (suppressed: 12 from 8)
==4364== 
==4364== 42 errors in context 1 of 2:
==4364== Invalid free() / delete / delete[]
==4364==    at 0x40057F6: free (vg_replace_malloc.c:325)
==4364==    by 0x8048A95: destroy_candidate (candidate.c:107)
==4364==    by 0x8048752: main (driver.c:44)
==4364==  Address 0x401e1b8 is 0 bytes inside a block of size 120 free'd
==4364==    at 0x40057F6: free (vg_replace_malloc.c:325)
==4364==    by 0x8048A95: destroy_candidate (candidate.c:107)
==4364==    by 0x8048752: main (driver.c:44)
==4364== 
==4364== 
==4364== 42 errors in context 2 of 2:
==4364== Invalid read of size 1
==4364==    at 0x400730E: strcmp (mc_replace_strmem.c:426)
==4364==    by 0x8048A7F: destroy_candidate (candidate.c:106)
==4364==    by 0x8048752: main (driver.c:44)
==4364==  Address 0x401e1b8 is 0 bytes inside a block of size 120 free'd
==4364==    at 0x40057F6: free (vg_replace_malloc.c:325)
==4364==    by 0x8048A95: destroy_candidate (candidate.c:107)
==4364==    by 0x8048752: main (driver.c:44)


void destroy_candidate()
{
    size_t i = 0;
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) {
        if(strcmp(candidate_names[i], "Invalid candidate") != 0) {
            free(candidate_names[i]);
        }
    }
}

EDIT with code from main.c =====================

typedef struct Candidate_data_t {
    size_t candidate_data_id;
    Candidates_t *candidate;
} Candidate_data;

static Candidate_data* create_candidate_data(Candidates_t *candidate, size_t i);
static void destroy_candidata_data(Candidate_data *cand_data);

int main(void)
{
    Candidates_t *candidate = NULL;
    Candidate_data *cand_data[NUMBER_OF_CANDIDATES] = {0};
    size_t i = 0;

    load_candidates();

    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) {
         candidate = create_candidates(i);
         if(candidate == NULL) {
             fprintf(stderr, "Cannot failed to initalize candidate [ %d ]\n", i);
         }

         /* Create array of candidates */
         cand_data[i] = create_candidate_data(candidate, i);
         fill_candidates(cand_data[i]->candidate);
    }

    /* Display each candidate */
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) {
        display_candidate(cand_data[i]->candidate);
        printf("\n");
    }

    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) {
        destroy_candidata_data(cand_data[i]);
    }

    return 0;
}

static Candidate_data* create_candidate_data(Candidates_t *candidate, size_t id)
{
    Candidate_data *cand_data = NULL;

    cand_data = malloc(sizeof *cand_data);

    if(cand_data == NULL) {
        fprintf(stderr, "Failed to allocate memory [ %s ]\n",
                strerror(errno));

        return NULL;
    }
    cand_data->candidate_data_id = id;
    cand_data->candidate = candidate;

    return cand_data;
}

static void destroy_candidata_data(Candidate_data *cand_data)
{
    destroy_candidate(cand_data->candidate);
    free(cand_data);
}
+7  A: 

Have a look at the getline() man page.

If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)

At the end of your program, you need to loop over your candidate_names array and call free on non NULL entries but in that case you must not do candidate_names[i] = "Invalid candidate";as @pmg pointed in his answer as you would try to free a string literal.

Pay also attention to:

Alternatively, before calling getline(), *lineptr can contain a pointer to a malloc(3)-allocated buffer *n bytes in size. If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary.

In either case, on a successful call, *lineptr and *n will be updated to reflect the buffer address and allocated size respectively.

Gregory Pakosz
+4  A: 

getline() allocates space for the line it just read, calling malloc() for you behind the scenes. You store these line buffers in the candidate_names array, but never free it. The leak isn't the file pointer - you free that just fine. It's the lines you read from the file, which need to be freed elsewhere, when you're done using them.

bdonlan
A: 

I don't think this is correct.

getline(&candidate_names[i], &n ,fp);

No reason to pass a pointer to an integer.

Novikov
getline is a POSIX standard function with prototype ` ssize_t getline(char **lineptr, size_t *n, FILE *stream);`. Passing a pointer-to-a-size_t is correct.
bdonlan
whoops, for some reason I was thinking of C++ fstream getline.
Novikov
+1  A: 

I see that you have two different macros NUMBER_OF_CANDIDATES and NAME_SIZE. Looks like trouble.

Jens Gustedt
That may be so, but it doesn't answer the question... This would probably be better as a comment, not an answer.
bdonlan
It's not *the* problem, but it is a problem, especially if `NUMBER_OF_CANDIDATES` ever grows larger than `NAME_SIZE`.
Karl Bielefeldt
#define NAME_SIZE 80. Sorry left that out.
robUK
@bdonlan: this could just have been *the* problem, since than you would have corrupted memory, undefined behavior, and anything can happen.
Jens Gustedt
+6  A: 

What is candidate_names? It's an array of pointers.
When you do

candidate_names[i] = "Invalid candidate";

you assign the pointer to a string literal. Maybe later in the program you want to free it. That's a NO-NO!

In any case, the previous value of candidate_names[i] is lost. If the value was not NULL, you just leaked some memory.

pmg
I thought was perfectly ok. Isn't the string literal a pointer? And I am assigning that pointer to the an element of the array of pointers to char. I am not allocating any memory, so there is no need to free. This would be freed by the O/S once the program exits as its allocated on the stack. Am I correct? Thanks.
robUK
good catch, +1 and I updated my answer to reflect it
Gregory Pakosz
Assuming your candidate names are long enough, try `strcpy(candidate_names[i], "Invalid candidate");` instead
pmg
@robUK: The point is that the string literal's storage is in the text segment of your executable. It was not allocated with malloc from the heap. You can't free it.
JeremyP
@robUK: you don't allocate but `getline()` does. That memory needs to be freed or valgrind complains.
pmg
@JeremyP, I understand now. So the string literal was allocated on the stack. So trying to free it would end up with an 'Invalid Free' error. As it was never allocated on using malloc. Thanks.
robUK
@robUK: String literals aren't allocated on the stack, but you're right that the point is that they're not allocated with `malloc`.
jamesdlin
@JeremyP, If string literals are allocated on the stack, there where do they reside? A string literal is a pointer that must have some area in memory. Thanks.
robUK
@robUK: maybe this SO post will help ... ( http://stackoverflow.com/questions/1966920/more-info-on-memory-layout-of-an-executable-program-process )
pmg
@robUK: String literals are not allocated on the stack, they are "allocated" as part of the code of your program by the compiler.
JeremyP
+1  A: 

You are allocating memory inside getline(). You are never freeing that memory. This is what valgrind is telling you: that you have seven (== NUMBER_OF_CANDIDATES) blocks that you have not freed.

Closing the file pointer does not free the memory that getline() allocated.

You need to do something like this at the end of load_candidates():

for(int i = 0; i < NUMBER_OF_CANDIDATES; i++)
{
    free(candidate_names[i]);
}

EDIT

In your edit you are freeing null pointers. Try:

void destroy_candidate()
{
    size_t i = 0;
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) {
        if ( (candidate_names[i] != NULL) && (strcmp(candidate_names[i], "Invalid candidate") != 0) ){
            free(candidate_names[i]);
        }
    }
}
Andy Johnson
+1  A: 

getline allocates memory which you store in your candidate_names array of pointers. Those pointers are not getting freed. When you are done with them, you should call:

for(i = 0; i < NUMBER_OF_CANDIDATES; i++)
{
    if (strcmp(candidate_names[i], "Invalid candidate") != 0)
        free(candidate_names[i]);
}

Additionally, that array should be declared as:

static char *candidate_names[NUMBER_OF_CANDIDATES];

And right before your getline you need:

candidate_names[i] = NULL;

NAME_SIZE isn't needed because that memory is dynamically allocated, unless you are using it elsewhere for input validation or something.

Karl Bielefeldt
I have updated my question. I have added a function to destroy_candidate() and free all memory. However, i am freeing all allocated memory. 'No leaks possible'. However, I am getting some errors 'Invalid free'. Thanks.
robUK
Could you post your `main`?
Karl Bielefeldt
@Karl, I have added my main function to the edited question. However, there is some other function in there. I kept them in case they were needed in finding a solution. Thanks.
robUK
Your `destroy_candidate` call doesn't match the definition you posted earlier.
Karl Bielefeldt
@Karl.That was the complete code. However, it did do a lot more. I just posted a sample in the ordinal question. I will try and sort this out myself. Looks like I will just have to take it apart. Thanks.
robUK