I have looked around for posts regarding valgrind memory leaks when using getline().
essentially what my code does is read in a line using getline().
int main(){
LL A;
LLinit(&A);
size_t len = 5;
char *lineOrigin = (char *)malloc(len);
char *line_ptr = lineOrigin;
getline(&line_ptr,&len,stdin);
int status, val;
while( (status = sscanf(line_ptr,"%d",&val) ) >= 0){
printf("%d\n",status);
//if(isBadInput(line_ptr)){...}
if(status == 0){
printf("ENCOUNTERED BAD INPUT. STOPPING\n");
return 1;
}
Node *node_ptr = (Node *)malloc(sizeof(Node));
node_ptr->val = val;
append(&A,node_ptr);
line_ptr = lookPastSpace(line_ptr);
}
printLL(&A);
freeLL(&A);
free(lineOrigin);
return 0;
}
As you can see, I allocate some memory for char *lineOrigin, and then have a second pointer *line_ptr, which I make point to *lineOrigin, that I mutate later in the code.
This block of code pretty much reads in integers from stdin and stores them in order into a LinkedList.
each time sscanf reads an int into &val, I use lookPastSpace() to move *line_ptr over the non-space characters in the string until i encounter another space, then i move the pointer one more notch over, so that line_ptr now points to the next place in the string where a new int can be read in
line_ptr = "123 456 789\n"
line_ptr = lookPastSpace(line_ptr)
line_ptr = "456 789\n"
...
line_ptr = "" -->sscanf stops.
Afterwords, I walk down the linked list and free every allocated Node, and since the nodes have no contents with allocated memory, thats as far as I have to go to free the memory of the LL.
Then, later, i free char *lineOrigin, and this presumably should work, since it IS the originally allocated buffer, but it does not.
tylerjgabb@lectura:~/HW6/medianDir$ make mem
valgrind median
==11827== Memcheck, a memory error detector
==11827== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11827== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==11827== Command: median
==11827==
123 456 789
1
1
1
[123, 456, 789]
==11827== Invalid free() / delete / delete[] / realloc()
==11827== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827== by 0x400B37: main (in /p3/ht/tylerjgabb/HW6/medianDir/median)
==11827== Address 0x51ff040 is 0 bytes inside a block of size 5 free'd
==11827== at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11827== by 0x4EA5F9A: getdelim (iogetdelim.c:106)
==11827== by 0x400A73: main (in /p3/ht/tylerjgabb/HW6/medianDir/median)
==11827==
==11827==
==11827== HEAP SUMMARY:
==11827== in use at exit: 13 bytes in 1 blocks
==11827== total heap usage: 5 allocs, 5 frees, 66 bytes allocated
==11827==
==11827== LEAK SUMMARY:
==11827== definitely lost: 13 bytes in 1 blocks
==11827== indirectly lost: 0 bytes in 0 blocks
==11827== possibly lost: 0 bytes in 0 blocks
==11827== still reachable: 0 bytes in 0 blocks
==11827== suppressed: 0 bytes in 0 blocks
==11827== Rerun with --leak-check=full to see details of leaked memory
==11827==
==11827== For counts of detected and suppressed errors, rerun with: -v
==11827== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
tylerjgabb@lectura:~/HW6/medianDir$
I'm a little stuck, and confused. Any ideas whats going on?
--------------CLARIFICATION ON TYPEDEFS AND LL FUNCTIONS----------------- LLfuncs.c
#include "LinkedListHeader.h"
#include <stdlib.h>
void LLinit(LL *LL_ptr){
LL_ptr->head_ptr = NULL;
LL_ptr->tail_ptr = NULL;
LL_ptr->length = 0;
}
void freeLL(LL *LL_ptr){
Node *curr_ptr = LL_ptr->head_ptr;
while(curr_ptr != NULL){
Node *next_ptr = curr_ptr->next_ptr;
free(curr_ptr);
curr_ptr = next_ptr;
}
}
above is only a small portion of my LLfuncs.c
LinkedListHeader.h
/* This is a header file containing typedefs and macros for linked lists *Tyler J Gabb
*For assignment 6a. Shuffle.
*/
#include <stdio.h>
#define showLocs(LL) printf("head_ptr = %p, tail_ptr = %p length = %d\n",LL.head_ptr,LL.tail_ptr,LL.length)
typedef struct LinkedListNode {
int val;
struct LinkedListNode *next_ptr;
} Node;
typedef struct LinkedList {
Node *head_ptr;
Node *tail_ptr;
int length;
} LL;
LLinit() initializes any recently declared LL type, to have head and tail at 0x0, and a length of 0. This is conducive to other functions which mutate an LL
--------------------------- SOME ADDITIONAL INTERESTING INFO-------------
If the size of the input string is less than the original value of len, then I don't get memory leak (sometimes)
You essentially have the following pattern:
char *lineOrigin, *line_ptr;
size_t len = 5;
lineOrigin = malloc(len);
line_ptr = lineOrigin;
getline(&line_ptr, &len, stdin);
Because getline()
will reallocate the buffer whenever necessary, after the getline()
call, lineOrigin
may not be valid anymore.
Essentially, you are keeping a copy of an old pointer in lineOrigin
that is possibly freed by the getline()
call. Instead of freeing the current buffer possibly reallocated by getline()
, line_ptr
, you mistakenly (use) and free()
some old value no longer valid: lineOrigin
.
The correct way to write the pattern is simple. You start by defining the line pointer as NULL
and its allocated size zero. I call the allocated size line_max
, with line_len
reflecting the length of the current line, but you can obviously use whatever variable naming you find most maintainable.
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
You read one or more lines using
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len > 0) {
/* You have a line in line_ptr */
} else
if (ferror(stdin)) {
/* Error reading from standard input (rare!) */
} else
if (feof(stdin)) {
/* No more data to read from standard input. */
}
You can repeat the above as many times as you want, but you need to realize that the value of line_ptr
and line_max
may vary from one call to the next.
After you are done, free the line buffer. Because free(NULL);
is always safe, this is safe to do even if no actual lines were read:
free(line_ptr);
line_ptr = NULL;
line_max = 0;
Now, clearing the variables is not strictly necessary, but I have found that it is a good practice, and may occasionally even help with debugging.
Also note that it is completely okay to free the buffer and clear the variables as above even just before a getline()
call, because getline()
does not care whether it gets a NULL pointer with zero size, or a previously allocated buffer with a nonzero size.