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);
}
If free() is not used in a program the memory allocated using malloc() will be de-allocated after completion of the execution of the program (included program execution time is relatively small and the program ends normally).
Consider the following prototype of free() function which is used to free memory allocated using malloc() or calloc() or realloc(). void free ( void *ptr);
Double free errors occur when free() is called more than once with the same memory address as an argument. Calling free() twice on the same value can lead to memory leak.
When you exit the program, all allocated memory is reclaimed by the OS (both the stack and the heap). Your program doesn't leave any footprint in the RAM, unless you work outside the program's memory through buffer overflows and such.
No, malloc() will not release the memory when the function terminates. Save this answer.
The free() function in C library allows you to release or deallocate the memory blocks which are previously allocated by calloc(), malloc() or realloc() functions. It frees up the memory blocks and returns the memory to heap. It helps freeing the memory in your program which will be available for later use.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With