Hi I was trying to write a function that takes in the head of a linked list and free all the memory it allocated. Here is the original code I wrote.
void clear_nodes(List *h) {
if (!h->next) {
free(h);
***h = NULL;***
}
else {
clear_nodes(h->next);
clear_nodes(h);
}
}
However it does not work. So I changed it to
void clear_nodes(List *h) {
if (!h->next) {
free(h);
}
else {
clear_nodes(h->next);
***h->next = NULL;***
clear_nodes(h);
}
}
and now it worked.
Notice the difference between the two, I just wonder why can't I make the pointer to null directly. Is it because it's a local variable or something? But marking a pointer to null should make the pointer point to some memory address that I can't access right? Why would it matter if I do it locally or globally.
Thank you guys in advance.
I would probably do this NON-RECURSIVELY
void clear_nodes(List **h) {
List * copy = *h;
while( (copy = (*h)->next) != NULL){
free(*h);
*h = NULL;
*h = copy;
}
}
Recursivity is generally slower than looping. If you can try to design without recursive routines.
Since the value of h was passed, and not the location, you are not changing the pointer - you are changing a copy of the pointer.
If you changed your function to
void clear_nodes(List **h) {
if ((*h)->next == NULL) {
free(*h);
*h = NULL;
}
else {
clear_nodes(&((*h)->next));
clear_nodes(h);
}
}
You could change it "locally". I think I got that second part right... didn't try to compile / run it. I am positive about the first part though.
Related
I have been building this hash table function that is given an array it gets a word from and a pointer to an array that it fills with my linked list struct. It compiles correctly but I get a segmentation fault at - *hashTable[hashVal] = *newNode; .
void hashTableCreate(char *array, list *hashTable[]) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable[hashVal]).word == temp ) {
(*hashTable[hashVal]).count = (*hashTable[hashVal]).count+1;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
*hashTable[hashVal] = *newNode;
}
}
}
It's called like this:
void timeStructures(char *newArray) {
list *hashTable[3000];
hashTableCreate(newArray, hashTable);
}
I know this is to do with the way I am using my pointers but I can't figure it out. Any help would be appreciated.
There are several problems on this code:
You seem you are using extra asterisks when assigning newNode to hashTable.
You can use ++ operator to increment count, and use -> to access a member of a pointer to struct.
getWord is unknown to me, but seems impossible it can return a pointer to a string contained inside an element of hashTable, which means the next if will always be false (newNode's word is a copied string, not a pointer to array's word), as it compares memory addresses instead of strings.
You never free temp neither hashTable elements, which seems another misconception of what you are doing here.
As noted by #kaylum , you should initialize hashTable so you avoid comparing against unassigned array elements, neither modifying count on them.
hashTable has to be passed as a pointer to hashTableCreate, or this will work on a copied version of it:
Try this:
void hashTableCreate(char *array, list *(*hashTable[])) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable)[hashVal] != NULL &&
strcmp((*hashTable)[hashVal]->word, temp) == 0 ) {
(*hashTable)[hashVal]->count++;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
(*hashTable)[hashVal] = newNode;
}
free(temp);
}
}
void timeStructures(char *newArray) {
list *hashTable[3000];
int i;
// Initialize hashTable pointers to NULL
for(i = 0; i < 3000; i++) {
hashTable[i] = NULL;
}
hashTableCreate(newArray, &hashTable);
// Free hashTable elements, malloc'ed at hashTableCreate
for(i = 0; i < 3000; i++) {
if(hashTable[i] != NULL) {
free(hashTable[i]);
}
}
}
Note 1: you never check if hashVal is higher than 3000, which might be fine if you know what you are doing, aka, you know hashingFunc will never return 3000 or higher. If you do not, you'll need to care about growing hashTable appropriately.
Note 2: executing getArrayLength on each loop might be a performance problem for large lists. You probably prefer to create a variable to contain its value and execute it only once.
Note 3: It might be some easier if you declare hashTable as a plain list of elements, not a list of pointers to elements, like list hashTable[3000];, but I don't know exactly what you're looking for. Also, it will always waste 3000 list elements of memory, so might be a performance problem.
My app is use in stlinux (sh4) and unfortunately valgrind does not support sh4 cpu.
since I saw memory leak with my app, I had used mtrace, and it confirmed that some memory is not free. The problem is, variable of malloc used in the return, therefore I do not have any idea, how could I free it (since if it would be free, then returning in the functions is meaningless)?
I had written cs_malloc (put bellow code from oscam-simple.c in above link), mtrace log says, that in line:
*tmp = malloc (size);
memory is not free
/* This function encapsulates malloc. It automatically adds an error message to the log if it failed and calls cs_exit(quiterror) if quiterror > -1.
result will be automatically filled with the new memory position or NULL on failure. */
void *cs_malloc(void *result, size_t size, int32_t quiterror){
void **tmp = result;
*tmp = malloc (size);
if(*tmp == NULL){
cs_log("Couldn't allocate memory (errno=%d %s)!", errno, strerror(errno));
if(quiterror > -1) cs_exit(quiterror);
} else {
memset(*tmp, 0, size);
}
return *tmp;
}
And then for malloc, I call it, like this:
// create the AES key entry for the linked list
if(!cs_malloc(&new_entry, sizeof(AES_ENTRY), -1)) return;
Please take a look at these 3 functions (which malloc is not free , and as other users said, valgrind claim that these codes cause memory leaks module-datastruct-llist.c
The memory leaks cause by 3 different parts:
in below codes "new" would never free , but since it use in return of that function, I don't have idea, how could I free it:
LL_NODE* ll_append_nolock(LLIST *l, void *obj)
{
if (l && obj) {
LL_NODE *new;
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
if (l->last)
l->last->nxt = new;
else
l->initial = new;
l->last = new;
l->count++;
return new;
}
}
also "l" use in below function, again since it use in return function, I have no idea how to free it. :
LLIST *ll_create()
{
LLIST *l = cs_malloc(&l, sizeof(LLIST), 0);
pthread_mutex_init(&l->lock, NULL);
return l;
}
same story with new :
LL_NODE *ll_prepend(LLIST *l, void *obj)
{
if (l && obj) {
LL_NODE *new;
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
ll_lock(l);
new->nxt = l->initial;
l->initial = new;
if (!l->last)
l->last = l->initial;
l->count++;
ll_unlock(l);
return new;
}
return NULL;
}
For more functions you could see module-datastruct-llist.c
Would highly appreciate, if any expert tell me, how could I fix that memory leak (if you feel, cs_malloc should be rewritten, or need to add new function, please write the source code you are meaning.
The most common implementations of malloc use heap memory, which is global, so it's very common to have storage allocated in one place passed around between a number of functions before it is finally freed.
Now, there are for instance calls to ll_append_nolock where you ignore the malloced return. I.e.
ll_append_nolock(it->l, obj);
so to avoid a leak you need to do what you do in other places, i.e let the calling function receive the allocated memory into a pointer:
LL_NODE *n = ll_append_nolock(l, obj);
/* do stuff with "n", which points to memory allocated under the name of "new" */
free(n);
And when you're through with n (which as noted above points to the storage allocted under the name "new", that is: same memory, different names), you free it.
HTH.
In your function cs_malloc the first parameter is result however you never assign to it in the function cs_malloc.
Later you use cs_malloc like this
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
which will not work since since "new" is left uninitialized
you should either assign to result in your cs_malloc or just return the block in cs_malloc, if you fail to allocate just return NULL instead.
e.g.
void *cs_malloc(size_t size, int32_t quiterror)
{
void* tmp = calloc(1,size);
if(tmp == NULL)
{
cs_log("Couldn't allocate memory (errno=%d %s)!", errno, strerror(errno));
if(quiterror > -1) cs_exit(quiterror);
}
return tmp;
}
and
if (new = cs_malloc(sizeof(LL_NODE),-1))
{
new->obj = obj;
}
else
{
return NULL;
}
#Anders
Thanks for reply , i would take consideration your note ,an would change it as u described to see memory leaks how it's goes...
How , this line should be change to the new cs_malloc function u had written:
1.
LLIST *l = cs_malloc(&l, sizeof(LLIST), 0);
pthread_mutex_init(&l->lock, NULL);
return l;
2.
if(!cs_malloc(¶,sizeof(struct read_thread_param), -1)) return FALSE;
para->id=i;
I have a problem with this small program. It added some value to list. If I uncomment //printf("%d",first->val); the program gives error. Everything seems to be ok ;(
#include <stdio.h>
#include <stdlib.h>
typedef struct element {
struct element *next;
int val;
} el_listy;
el_listy *first = 0;
void add_to_list(el_listy *lista, int value)
{
if(lista == 0)
{
lista = malloc (sizeof(el_listy));
lista->val = value;
lista->next = 0;
printf("added as first \n");
}
else
{ printf("added as fsecond \n");
el_listy *wsk = lista,*tmp;
while(wsk->next != 0) wsk = wsk->next;
tmp = malloc (sizeof(el_listy));
tmp->val = value;
tmp->next = 0;
wsk->next = tmp;
}
}
int main ()
{
add_to_list(first,2);
add_to_list(first,4);
//printf("%d",*first->val);
system("pause");
return 0;
}
first->val is just like (*first).val, you can't use them both. also, as missingno said, add_to_list never changes first you should pass it's address as argument, not the pointer itself, meaningadd_to_list(&first,4); (and change the implementation of add_to_list as well)
Your program never changes the value of first. It remains a null pointer and thus gives an error when dereferenced.
-> already follows a pointer, so the * tries to treat first as pointer to pointer to el_listy. You might find cdecl helpful.
You should use either (*first).val or first->val. Otherwise you get the wrong level of indirection.
Yes, it's a simple mistake.
fitsr won't change after *add_to_list()* function is called.
You should define function like this:
add_to_list(El_list **lista, ...)
this might be a bit long so my apologies.
consider the following code (i've left some irrelevant parts from it). this code receives a pointer to a struct (BoardP theBoard), x & y coords and a value.
the goal is to place the value in a 2D array that is found in the struct.
if the coords are out of bounds, i have to increase the size of the table, copy old data to new data and place the value in its place.
well this code works the first call but in the second call it crashes and writes:
*** glibc detected *** ./b: double free or corruption (top): 0x092ae138 ***
i couldn't find an answer to it and i hope you will help.
These are the calls from main()
BoardP p = CreateNewBoard(10,10);
PutBoardSquare(p,10,5,'X');
PutBoardSquare(p,5,10,'O');
Boolean PutBoardSquare(BoardP theBoard, int X, int Y, char val) {
if (inBounds(X,Y,theBoard->_rows,theBoard->_cols)) {
theBoard->_board[X * theBoard->_cols + Y] = val;
return TRUE;
}
else {
int newRows = (X>=theBoard->_rows) ? (2*X) : theBoard->_rows;
int newCols = (Y>=theBoard->_cols) ? (2*Y) : theBoard->_cols;
BoardP newBoard = CreateNewBoard(newCols,newRows); //this creates a new Board with the new dimensions
if (newBoard == NULL) {
//ReportError(MEM_OUT);
return FALSE;
}
else {
copyData(theBoard,newBoard);
freeBoardArray(&theBoard->_board[0]); //free old array
theBoard->_board = newBoard->_board; //old array point to new array
FreeBoard(newBoard); //free the temp copy THIS CAUSES THE PROBLEM
PutBoardSquare(theBoard,X,Y,val);//recursion, will be in bounds now
return TRUE;
}
}
}
These are the Free functions:
void FreeBoard(BoardP board) {
if (board != NULL) {
printf("FREE 1\n");
//free the board array:
if (board->_board != NULL) {
printf("FREE 2\n");
freeBoardArray(&board->_board[0]);
printf("FREE 3\n");
}
free(board);
}
}
static void freeBoardArray(char * arrP) {
free(arrP); //**PROGRAM CRASH HERE**
}
This is how i create a new board:
BoardP CreateNewBoard(int width, int high) {
BoardP board = (BoardP) malloc(sizeof(Board));
if (board != NULL) {
board->_board = allocateBoardArray(high,width);
if ( board->_board == NULL) {
FreeBoard(board);
//TODO make file ReportError(MEM_OUT);
return NULL;
}
initializeBoard(board,high,width,X_SIGN,SPACE);
return board;
}
else {
FreeBoard(board);
//TODO make file ReportError(MEM_OUT);
return NULL;
}
}
static char* allocateBoardArray(int row, int col) {
char* newBoard = (char*) malloc(row * col * sizeof(char));
if (newBoard == NULL) {
return NULL;
}
return newBoard;
}
this is BoardP:
typedef struct Board* BoardP;
You have to free memory which you have allocated and no longer want to hold a reference too.
from your code i can see the following line.
theBoard->_board = newBoard->_board;
Now you maintain reference to a allocated pointer and then free that same pointer itself.
Example code:
char *foo()
{
char *ref1;
char *ref2;
ref1 = malloc(256);
ref2=ref1;// Holding reference to a pointer in another pointer
strcpy(ref1,"stackoverflow");
printf("%s %s",ref1,ref2); // This prints stackoverflow twice
free(ref1); // This is valid but you can access ref2 or ref1 after this point
return ref2; /// This will cause problems
}
Try this:
copyData(theBoard, newBoard);
/* swap the _board pointers */
char * b = theBoard->_board;
theBoard->_board = newBoard->_board;
newBoard->_board = b;
FreeBoard(newBoard); /* cleanup the temp struct and the old array */
This errors says that you are trying to free the memory which is already freed by you. What i am suspecting here is this block of code
if (board != NULL) {
printf("FREE 1\n");
//free the board array:
if (board->_board != NULL) {
printf("FREE 2\n");
freeBoardArray(&board->_board[0]);
printf("FREE 3\n");
}
free(board);
once you are freeing the part of structure freeBoardArray(&board->_board[0]); and then you are freeing the whole structure free(board);, and it looks to me causing the problem.Why you passing the address of the _board pointer?I wrote the code on the same line of code,which causing the problem.
struct a{
int * next;
};
int main(){
struct a *aptr = (struct a *)malloc(sizeof(struct a));
aptr->next=(int *)malloc(5*sizeof(int));
free(&aptr->next);
free(aptr);
return 0;
}
this code will cause the same issue as you shown. Now again try this code after removing '&' from free(&aptr->next);statement.It will work fine.
So i think you got a clue where you have to modify.
Running this code under valgrind will tell you exactly on which line you a.) first freed the memory and b.) when you tried to free it again.
It will also tell you if you try and access any addresses which are inside a block that you have freed.
I have an exam tomorrow and I was trying to understand this doubly linked list example that the instructor placed on the class website but I'm having a hard time understanding a bit of it...
Here's the code:
#include <stdio.h>
#include <stdlib.h>
typedef struct dl {
int key;
float value;
struct dl *next;
struct dl *prev;
} DL;
DL *insert(int c, float f, DL *l) {
DL *new = (DL*) malloc(sizeof(DL));
if (new == NULL) exit(-1);
new->key=c; new->value=f;
if (l==NULL) {
new->next=NULL; new->prev=NULL;
}
else if (l->key < c) {
while((l->next != NULL) && (l->next->key < c)) { l=l->next; }
new->next=l->next; l->next=new; new->prev=l;
if (new->next != NULL) {
new->next->prev=new;
}
}
else {
while((l->prev != NULL) && (l->prev->key > c)) { l=l->prev; }
new->prev=l->prev; l->prev=new; new->next=l;
if(new->prev != NULL) {
new->prev->next=new;
}
}
return new;
}
int search(int c, float *f, DL **lptr) {
if (*lptr == NULL) return 0;
if (c < (*lptr)->key) {
while(((*lptr)->prev!=NULL)&&((*lptr)->prev->key >= c)) {
(*lptr)=(*lptr)->prev;
}
}
else if (c > (*lptr)->key) {
while(((*lptr)->next!=NULL)&&((*lptr)->next->key <= c)) {
(*lptr)=(*lptr)->next;
}
}
if ((*lptr)->key == c) {
*f = (*lptr)->value;
return 1;
}
return 0;
}
void printList(DL *l) {
if (l == NULL) return;
while (l->prev != NULL) { l=l->prev; };
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
}
int main(void) {
DL *list=NULL;
float f;
list=insert(3,5.6,list); list=insert(4,5.3,list);
list=insert(7,3.6,list); list=insert(1,7.7,list);
list=insert(9,2.3,list); list=insert(0,9.0,list);
printList(list);
if (search(3,&f,&list)) {
printf("Found %f.\n",f);
}
else {
printf("Not found.\n");
}
printList(list);
return 0;
}
An here's the output:
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
Found 5.600000.
0,9.000000
1,7.700000
3,5.600000
4,5.300000
7,3.600000
9,2.300000
What I don't get is the "search" function. The list being passed is a pointer to a pointer of DL, right? And we are looking for a number, for that we keep doing (*lptr) = (*lptr)->next (or prev) to iterate through the whole list. What I don't get is why the second call to printList() prints the whole list... After the search() call has been made, shouldn't the "list" only have the elements after the one we looked for? The pointer was changed, how come when we return from search() the pointer is restored to the first element and the whole list is printed?
This is what I don't get cause if I change the search() function and add (*lptr) = NULL in the first line, the second call to printList() will not print anything, cause the pointer was changed, it is NULL now, there's nothing to print. Why doesn't (*lptr) = (*lptr)->next has a similar effect? The pointer is also being changed to the next one, shouldn't the second printList() call only print the remaining elements in the list?
EDIT:
Every answer seems to be the correct one and I'm going to sort it by "oldest" and accept the "fastest" one, don't be mad, I need to have some criteria. I could go on and see which answered provided better insight on the issue but it's irrelevant because I already know everything that was said. I was just stupid enough to not even look to the printList() function and assumed it was ok, I also assumed that the error was somewhere on the search() function. But I knew I was right, I knew the pointer was being change and the list couldn't print everything, but I understand why now...
printList rewinds the list before printing it.
while (l->prev != NULL) { l=l->prev; };
If it didn't have the above line, it would just print the things after the found element.
This line return pointer back:
while (l->prev != NULL) { l=l->prev; };
And those do the printing:
while(l != NULL) {
printf("%d,%f\n",l->key,l->value);
l=l->next;
}
And there is much better approach of doing this, just by adding the additional field or even two which will always point at the beginning and end of the list.
As far as I can read (and like rmeador commented, it is pretty awful code), the search call does modify the list pointer to point to the found element.
The trick is the printList function.
The first thing it does (other than checking for NULL) is this:
while (l->prev != NULL) { l=l->prev; };
So it basically follows the prev pointer back to the start of the list, so the actual printing starts from the start of the list even if it is passed a pointer to the middle or end of it.
In the printList() function you are going back from the found element using l = l->prev. Then you are printing all the contents.
What I don't get is why the second
call to printList() prints the whole
list... After the search() call has
been made, shouldn't the "list" only
have the elements after the one we
looked for? The pointer was changed,
how come when we return from search()
the pointer is restored to the first
element and the whole list is printed?
What you have is not really a pointer to the list, but a pointer to an element in the list. The first thing that the printList function does it to loop back through the prev references to find the first element of the list.
He backtrack the pointer inside the print:
while (l->prev != NULL) { l=l->prev; };
Remember that the list is doubly linked. Search doesn't change the list, just what part of it "list" is presently pointing to.