Deallocating dynamically linked list of struct in C - c

I have a bunch of structures that all look like
typedef struct {
A[1..100] *next; // this is not an array, just indicating A1 or A2 or A3 and so on
//other stuff that varies from struct to struct
} A[1..100] // A1, A2, and so on
I generate a few linked lists of different same type structures. Somewhere in a function, I allocate the memory with something like
A55 *struct_list;
A55 *next_in_list;
struct_list = (A55 *)malloc(sizeof(A55));
(*struct_list).next = NULL;
//some loop
next_in_list = (A55 *)malloc(sizeof(A55));
(*next_in_list).next = struct_list;
struct_list = next_in_list;
At the end of the loop, struct_list is a pointer to the end of the linked list.
I would like to have a single function that would free any list regardless of the structure that populates it. I feel the following might work, but I need something which will not break any rules and might be implementation safe:
void freeStruct(*void start){
void ** current, * next;
current = (void **) start;
do{
next = *current;
free(current);
current = (void **) next;
}while(current != NULL)
}
My question is whether NULL has the same numerical value for all pointers to all types, including struct. And, is there a better way of doing this without having to copy the same function 100 times for the different struct definitions?

NULL always has the same value: 0.
You can do what you want. The way to do it is to put the "next" pointer at the very beginning of the struct, so it is always in the same place for each struct.
If you have any more structure than a single "next" pointer, you should probably make a struct out of it all, and then put the struct at the beginning of each struct. For example, if you will make a doubly-linked list with both "prev" and "next" pointers, I suggest making a struct with the two pointers.
If each struct can simply be freed, you can just call the free() function on each pointer. If you sometimes need to run a cleanup function, you should make your universal linked-list freeing function take a pointer to a cleanup function, and call the cleanup on each struct as it is reached in the list.

You need to traverse through the entire list while freeing the pointers one at a time.

Related

Does the struct get freed this way in C?

I have the following struct which I use to implement a priority queue
struct q_element
{
//Declaration of struct members
int element;
int priority;
struct q_element *next_element;
};
and I use a pointer to a struct 'cur' to start from the first item in the queue
struct q_element* cur = start_element;
and keep moving until I find the one I want to delete from list.
while (cur->priority!=max_priority)
cur = cur->next_element;
Does the following line of code actually free the struct? Because 'cur' is a pointer to the struct I wasn't completely sure.
free(cur);
You need to pass a pointer to free, so free(cur) is the way to go, assuming that the struct itself has been allocated using malloc/calloc/realloc. Specifically, if you allocated your curr in the automatic memory (i.e. on the stack) you are not supposed to call free on it.
It looks like q_element is part of a linked list. Freeing the struct itself will not free other structs pointed to by it, so if you'd like to free the struct along with its tail, you need to write a loop.
Finally, when you free memory pointed to by some pointer in your program, it is a very good idea to assign NULL to the pointer that you freed to avoid accidental double-freeing and undefined behavior on accessing freed memory.

Deleting first node of linked list

I have to print a list of a set in c using linked lists (hence pointers). However,when I delete the first element of the list and try to print the list, it just displays a lot of addresses under each other. Any suggestions of what the problem might be? Thanks!
Delete function:
int delete(set_element* src, int elem){
if (src==NULL) {
fputs("The list is empty.\n", stderr);
}
set_element* currElement;
set_element* prevElement=NULL;
for (currElement=src; currElement!=NULL; prevElement=currElement, currElement=currElement->next) {
if(currElement->value==elem) {
if(prevElement==NULL){
printf("Head is deleted\n");
if(currElement->next!=NULL){
*src = *currElement->next;
} else {
destroy(currElement);
}
} else {
prevElement->next = currElement->next;
}
// free(currElement);
break;
}
}
return 1;
}
void print(set_element* start)
{
set_element *pt = start;
while(pt != NULL)
{
printf("%d, ",pt->value);
pt = pt->next;
}
}
If the list pointer is the same as a pointer to the first element then the list pointer is no longer valid when you free the first element.
There are two solutions to this problem:
Let all your list methods take a pointer to the list so they can update it when neccesary. The problem with this approach is that if you have a copy of the pointer in another variable then that pointer gets invalidated too.
Don't let your list pointer point to the first element. Let it point to a pointer to the first element.
Sample Code:'
typedef struct node_struct {
node_struct *next;
void *data;
} Node;
typedef struct {
Node *first;
} List;
This normally happens when you delete a pointer (actually a piece of the memory) which doesn't belong to you. Double-check your function to make sure you're not freeing the same pointer you already freed, or freeing a pointer you didn't create with "malloc".
Warning: This answer contains inferred code.
A typical linked list in C looks a little something like this:
typedef struct _list List;
typedef struct _list_node ListNode;
struct _list {
ListNode *head;
}
struct _list_node {
void *payload;
ListNode *next;
}
In order to correctly delete the first element from the list, the following sequence needs to take place:
List *aList; // contains a list
if (aList->head)
ListNode *newHead = aList->head->next;
delete_payload(aList->head->payload); // Depending on what the payload actually is
free(aList->head);
aList->head = newHead;
The order of operations here is significant! Attempting to move the head without first freeing the old value will lead to a memory leak; and freeing the old head without first obtaining the correct value for the new head creates undefined behaviour.
Addendum: Occasionally, the _list portion of the above code will be omitted altogether, leaving lists and list nodes as the same thing; but from the symptoms you are describing, I'm guessing this is probably not the case here.
In such a situation, however, the steps remain, essentially, the same, but without the aList-> bit.
Edit:
Now that I see your code, I can give you a more complete answer.
One of the key problems in your code is that it's all over the place. There is, however, one line in here that's particularly bad:
*src = *currElement->next;
This does not work, and is what is causing your crash.
In your case, the solution is either to wrap the linked list in some manner of container, like the struct _list construct above; or to rework your existing code to accept a pointer to a pointer to a set element, so that you can pass pointers to set elements (which is what you want to do) back.
In terms of performance, the two solutions are likely as close so as makes no odds, but using a wrapping list structure helps communicate intent. It also helps prevent other pointers to the list from becoming garbled as a result of head deletions, so there's that.

C: Pointer to pointer issue when I try to group a lot of things in it

I have a function that adds one item to a list that I created. If it's the first time and the list points to NULL, it allocates the list and completes it, returning the address. If it's not the first time, it adds another item and again returns the first item (by now I could disregard this return). The list and the function WORKS fine, here is the prototype:
typedef struct structTAppConsoleList {
char *text;
void (*cbFunction)(int);
int number; // This is the item number
struct structTAppConsoleList *previous;
struct structTAppConsoleList *next;
} TList;
TList *AppConsoleListAddItem(TList *p_list, const char *p_string, void (*p_funcPtr)(int));
So, somewhere in my code I have to create a lot of them and I'm trying to make it as the code below shows. Thing is, I can't make it work... I want to create something to group the lists I want to create and then use it in the function. The code below is an idea of what I'm trying to do. Consider only the part where I try to allocate the 3 lists, the rest is not important for this example.
TList *list1;
TList *list2;
TList *list3;
int main(void)
{
int i,j;
TList **groupMyLists;
TList *temp;
groupMyLists=malloc(sizeof(TList)*3);
*groupMyLists =(TList*)&list1;
*(groupMyLists+1)=(TList*)&list2;
*(groupMyLists+2)=(TList*)&list3;
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
**groupMyLists=temp; // my make won't let me do this
groupMyLists++;
}
}
I'm pretty sure that this would do it, but I can't compile it.
In my head, (*groupMyLists) would be the same as (&list1), (&list2), (&list3), the same way that (**groupMyLists) would be the same as (list1), (list2) and (list3). So why I can't do (**groupMyLists=temp)? Anyone?
I hope I made myself clear!! I's not easy to explain this madness I'm trying to do...
Change this line, you are using the wrong indirection.
*groupMyLists=temp;
In addition to the above two answers about the incorrect indirection of **groupMyLists you probably also want to assign the list1,list2,list3 pointers correct pointer values instead of writing garbage values into the allocated memory in groupMyLists i.e.
TList * groupMyList = malloc(sizeof(TList)*3);
list1 = &groupMyList[0];
list2 = &groupMyList[1];
list3 = &groupMyList[2];
but, this does not really match the rest of your code as it seems that AppConsoleAddListItem allocates the temp list so in that case your malloc would be incorrect as it should allocate the space for the pointers instead of space for the lists as in:
TList ** groupMyList = (TList **)malloc(sizeof(TList *)*3);
TList * temp;
if (!groupMyList) {
/* Print allocation error warning or handle in some proper fashion */
exit(1);
}
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
groupMyLists[j]=temp; // Here you now assign the pointer in temp to the memory for // pointers that you allocated above
}
list1 = groupMyList[0]; // Here we need to assign the list1,2,3 after the AppConsole calls
list2 = groupMyList[1]; // as these calls will changes the pointer addresses written into
list3 = groupMyList[2]; // groupMyList
Although I can not be sure exactly what you are trying to do there are several inconsistencies of pointers and indirections in your original code and the above two examples can hopefully be of some guidance
This would do the job:
**groupMyLists = *temp;
of copying one struct referenced by temp to another struct referenced by *groupMyLists.
But only if *groupMyLists would reference any valid memory, which is does not do - at least not from the source you posted.

C generic linked-list

I have a generic linked-list that holds data of type void* I am trying to populate my list with type struct employee, eventually I would like to destruct the object struct employee as well.
Consider this generic linked-list header file (i have tested it with type char*):
struct accListNode //the nodes of a linked-list for any data type
{
void *data; //generic pointer to any data type
struct accListNode *next; //the next node in the list
};
struct accList //a linked-list consisting of accListNodes
{
struct accListNode *head;
struct accListNode *tail;
int size;
};
void accList_allocate(struct accList *theList); //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList); //append data to the end of the accList
void removeData(void *data, struct accList *theList); //removes data from accList
--------------------------------------------------------------------------------------
Consider the employee structure
struct employee
{
char name[20];
float wageRate;
}
Now consider this sample testcase that will be called from main():
void test2()
{
struct accList secondList;
struct employee *emp = Malloc(sizeof(struct employee));
emp->name = "Dan";
emp->wageRate =.5;
struct employee *emp2 = Malloc(sizeof(struct employee));
emp2->name = "Stan";
emp2->wageRate = .3;
accList_allocate(&secondList);
appendToEnd(emp, &secondList);
appendToEnd(emp2, &secondList);
printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name); //cast to type struct employee
printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);
}
Why does the answer that I posted below solve my problem? I believe it has something to do with pointers and memory allocation. The function Malloc() that i use is a custom malloc that checks for NULL being returned.
Here is a link to my entire generic linked list implementation: https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation
The problem is this accList_allocate() and your use of it.
struct accList secondList;
accList_allocate(&secondList);
In the original test2() secondList is memory on the stack. &secondList is a pointer to that memory. When you call accList_allocate() a copy of the pointer is passed in pointing at the stack memory. Malloc() then returns a chunk of memory and assigns it to the copy of the pointer, not the original secondList.
Coming back out, secondList is still pointing at uninitialised memory on the stack so the call to appendToEnd() fails.
The same happens with the answer except secondList just happens to be free of junk. Possibly by chance, possibly by design of the compiler. Either way it is not something you should rely on.
Either:
struct accList *secondList = NULL;
accList_allocate(&secondList);
And change accList_allocate()
accList_allocate(struct accList **theList) {
*theList = Malloc(sizeof(struct accList));
(*theList)->head = NULL;
(*theList)->tail = NULL;
(*theList)->size = 0;
}
OR
struct accList secondList;
accList_initialise(secondList);
With accList_allocate() changed to accList_initialise() because it does not allocate
accList_initialise(struct accList *theList) {
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
I think that your problem is this:
You've allocated secondList on the stack in your original test2 function.
The stack memory is probably dirty, so secondList requires initialization
Your accList_allocate function takes a pointer to the list, but then overwrites it with the Malloc call. This means that the pointer you passed in is never initialized.
When test2 tries to run, it hits a bad pointer (because the memory isn't initialized).
The reason that it works when you allocate it in main is that your C compiler probably zeros the stack when the program starts. When main allocates a variable on the stack, that allocation is persistent (until the program ends), so secondList is actually, and accidentally, properly initialized when you allocate it in main.
Your current accList_allocate doesn't actually initialize the pointer that's been passed in, and the rest of your code will never see the pointer that it allocates with Malloc. To solve your problem, I would create a new function: accList_initialize whose only job is to initialize the list:
void accList_initialize(struct accList* theList)
{
// NO malloc
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
Use this, instead of accList_allocate in your original test2 function. If you really want to allocate the list on the heap, then you should do so (and not mix it with a struct allocated on the stack). Have accList_allocate return a pointer to the allocated structure:
struct accList* accList_allocate(void)
{
struct accList* theList = Malloc( sizeof(struct accList) );
accList_initialize(theList);
return theList;
}
Two things I see wrong here based on the original code, in the above question,
What you've seen is undefined behaviour and arose from that is the bus error message as you were assigning a string literal to the variable, when in fact you should have been using the strcpy function, you've edited your original code accordinly so.. something to keep in mind in the future :)
The usage of the word Malloc is going to cause confusion, especially in peer-review, the reviewers are going to have a brain fart and say "whoa, what's this, should that not be malloc?" and very likely raise it up. (Basically, do not call custom functions that have similar sounding names as the C standard library functions)
You're not checking for the NULL, what if your souped up version of Malloc failed then emp is going to be NULL! Always check it no matter how trivial or your thinking is "Ah sher the platform has heaps of memory on it, 4GB RAM no problem, will not bother to check for NULL"
Have a look at this question posted elsewhere to explain what is a bus error.
Edit: Using linked list structures, in how the parameters in the function is called is crucial to the understanding of it. Notice the usage of &, meaning take the address of the variable that points to the linked list structure, and passing it by reference, not passing by value which is a copy of the variable. This same rule applies to usage of pointers also in general :)
You've got the parameters slightly out of place in the first code in your question, if you were using double-pointers in the parameter list then yes, using &secondList would have worked.
It may depend on how your Employee structure is designed, but you should note that
strcpy(emp->name, "Dan");
and
emp->name = "Dan";
function differently. In particular, the latter is a likely source of bus errors because you generally cannot write to string literals in this way. Especially if your code has something like
name = "NONE"
or the like.
EDIT: Okay, so with the design of the employee struct, the problem is this:
You can't assign to arrays. The C Standard includes a list of modifiable lvalues and arrays are not one of them.
char name[20];
name = "JAMES" //illegal
strcpy is fine - it just goes to the memory address dereferenced by name[0] and copies "JAMES\0" into the memory there, one byte at a time.

Destroy deque using pointers and free in C

I'm trying to destroy my deque but somehow I fail with pointers. I have written the following code (deque is a pointer to a pointer, which points to the first element of the deque). The DequeItem's are structs with fields next (pointer to next element) and data (void *).
void deque_destroy(DequeItem **deque) {
DequeItem *temp;
DequeItem *item;
for (item = *deque; item != NULL; item = temp) {
printf("%d", *((int*)((item)->data)));
temp = item->next;
free(item);
}
}
The struct declaration is:
struct DequeItem {
void *data; // Data stored in the deque item
struct DequeItem *previous; // Pointer to the previous DequeItem in the ring
struct DequeItem *next; // Pointer to the next DequeItem in the ring
};
typedef struct DequeItem DequeItem;
Looks correct, well done to read out temp before you call free() on the item, that's a common beginner mistake you've avoided.
I think you need to provide more information about what goes wrong, and perhaps also the struct declarations.
Is the data member also dynamically allocated memory? If so, you might need a free(item->data); call, too depending on how it was allocated when the item was created.
As commenters have pointed out, your data pointer might be NULL so you should check it before printing:
if(item->data != NULL)
printf("%d\n", *(int *) item->data);
Note:
Simplification of the casting expression makes it easier to read.
Include line feed ('\n') in the printf() string to avoid buffering confusion and separating the values visually.
The problem was, that the next-pointer of the last element (back element) in the deque will point to the first element (front element) even though the first element has been destroyed. I fixed this by setting
(*deque)->previous->next = NULL
before the for-loop above. Thanks for help!

Resources