In brief, I am attempting to use a void pointer as a parameter to a function pointer, but am getting the compiler error "invalid use of void expression".
I have a doubly linked list (DLL) whose node structure is as follows:
typedef struct DL_LIST
{
uint16 tag; /* Object ID tag */
struct DL_LIST *previous;
struct DL_LIST *next;
void *object; /* A pointer to this node's object */
uint32 size; /* The size of this node's object, in bytes */
} DL_LIST;
I also have the following function which is used to delete a single such node:
void dl_delete(DL_LIST *node, void (*dl_destructor)(void*)) {
if (node != NULL) {
dl_extract(node); /* Removes the node from the list */
if (node->object != NULL) {
(*dl_destructor)(node->object);
free(node->object);
}
free(node);
}
}
where the node extraction function is:
DL_LIST *dl_extract(DL_LIST *node) {
if (node != NULL) {
if (node->previous != NULL) {
node->previous->next = node->next;
}
if (node->next != NULL) {
node->next->previous = node->previous;
}
node->previous = NULL;
node->next = NULL;
}
return node;
}
The idea here is to be able to pass a separate destructor function for each type of object that may be stored in a node. This destructor function takes a pointer to the object as a parameter, and is used to free any heap memory that is being used by children of the object.
The aforementioned error occurs when I try to call dl_delete() from a function designed to delete an entire DLL:
void dl_destroy(DL_LIST **list, void (*dl_destructor)(void*)) {
DL_LIST *marker;
DL_LIST *previous_node;
if (*list != NULL) {
previous_node = (*list)->previous;
while (previous_node != NULL) {
marker = previous_node->previous;
dl_delete(previous_node, (*dl_destructor)(previous_node->object));
previous_node = marker;
}
/* Code removed for brevity */
}
}
I have read this introduction to function pointers, but am still unable to determine how to remedy the problem. An explanation of what I am doing wrong would be most appreciated.
this line
dl_delete(previous_node, (*dl_destructor)(previous_node->object));
needs to be dl_delete(previous_node, dl_destructor);
also in dl_delete this line (*dl_destructor)(node->object);
should be dl_destructor(node->object);
also, just for safety, I like to check that my function pointers are not null before trying to make a call using them
so in dl_delete something like :-
if(dl_destructor!=NULL) dl_destructor(node->object);
Related
So, my task is to to write a full implementation of a Singly Linked List in C.
I wrote before implementations of a stack and a dynamic vector, but this time, the linked list confuses me a little because of the use of 3 different typedef.
I'll be glad to get your review and tips on my code.
I would make a test file as I always do, but I am having a hard time of writing one because of all the void * casts .
I won't add all the 14 functions, i'll add just the functions that I'm least sure of.
So we must follow the following typedefs and the given prototypes. So neither of them can be changed.
I also had to add a "dummy node" as the last node, which means there will be always a "dummy node" that will indicate that the one before it, is the "real" last node in the list. This is part of the instructions.
typedef struct slist slist_ty;
typedef struct slist_node slist_node_ty;
typedef slist_node_ty *slist_iter_ty;
This is my implementation of the structs:
They asked us to allow in theory any type of data in our nodes, that's why I wrote void *.
struct slist
{
slist_iter_ty head;
slist_iter_ty end;
};
struct slist_node
{
void *data;
slist_iter_ty next;
};
And these are the functions:
/* Creates an empty single-linked list and returns pointer to the head */
/* returns NULL on failure*/
/* Complexity: O(1) */
slist_ty *SlistCreate(void)
{
slist_ty *new_list = (slist_ty *)malloc(sizeof(slist_ty));
if (NULL == new_list)
{
fprintf(stderr, "Failed to allocate memory\n");
return(NULL);
}
new_list->head = NULL;
/* create a dummy node that will represent the end of the list */
new_list->end = (slist_node *)malloc(sizeof(slist_node));
if (NULL == new_list->end)
{
fprintf(stderr, "Failed to allocate memory\n");
free(new_list);
return(NULL);
}
new_list->end->data = NULL;
new_list->end->next = NULL;
return(new_list->head);
}
/* Deletes entire List */
/* Complexity: O(n) */
void SlistDestroy(slist_ty *slist)
{
slist_iter_ty temp = NULL;
assert(slist);
while(NULL != slist->head)
{
tmp = slist->head;
slist->head = temp;
free(temp);
}
free(slist->end);
slist->end = NULL;
free(slist);
slist = NULL;
}
/* Insters the element after the iterator, returns iterator to the new node */
/* TODO Undefined behaviour if iter is slist_END */
/* Complexity: O(1) */
slist_iter_ty SlistInsert(slist_iter_ty iter, void *data)
{
slist_iter_ty new_node = NULL;
assert(iter);
assert(iter->next);
assert(data);
new_node->data = data;
new_node->next = iter->next;
iter->next = new_node;
return(new_node);
}
/* Returns iterator to end of the list */
/* Complexity: O(1) */
slist_iter_ty SlistIteratorEnd(const slist_ty *slist)
{
slist_iter_ty iterator = slist->head;
assert (slist);
if (NULL == slist->head)
{
return(NULL);
}
while (NULL != iterator->next->data)
{
iterator = iterator->next;
}
return(iterator);
}
My question along the request to get a feedback is:
Should I free any new slist_iter_ty that I make ?
For example, I made an iterator of type slist_iter_ty in the last function, in order to help me to traverse the list. But I can't free the iterator because I need to return it as the return value.
I also made a new_node in the SlistInsertfunction.
Will it be freed as part of the SlistDestroy function?
Thanks.
slist - is the list. when you create this list you use malloc so when you want to destroy it you need to free the list.
also - you used malloc every time you used insert. so when you want to destroy the list, you need to empty it from all the nodes - so you will need to free node by node
i can see you doesn't use mallloc in slist insert - how can you keep the data without use malloc?
In destroy function
while(NULL != slist->head)
{
tmp = slist->head;
slist->head = temp;
free(temp);
}
I think what you meant is:
while(NULL != slist->head)
{
tmp = slist->head;
slist->head = slist->head->next;
free(tmp);
}
In insert function
slist_iter_ty new_node = NULL;
what you should write:
new_node = (slist_iter_ty) malloc(sizeof(slist_node));
in slist end function
slist_iter_ty SlistIteratorEnd(const slist_ty *slist)
you can just return (after you assert something :)) :
return (slist->end);
(otherwise it wouldent be O(1) it would be O(n))
I'm fairly new to coding in c and have stumbled upon a problem. The code below is what I have so far and the problem at hand is how to looping through the node job_list.
We have two nodes, the first one creates a basic job post with a reference number (an integer) and job name (a string - an array of characters) as parameters, and the second is a list that holds job posts with the job_node being one parameter and then the second one being a standard struct job_list * next parameter.
I think you mixed few things up..
singly link list (that's what i understand you are up to) is not a fixed size data structure. it even not an increasing space data structure. link list use exactly the amount of memory you want to hold in it.
link list is a list of nodes. you can create list - and it will be an empty list.
then, you can add nodes with your desired data and every node you insert - create a new node (on the heap = dynamically allocated) which will contain your data and will be linked to the list.
every data node in the list will have a node_next it will point to, and node_prev which will point at this particular node(head and tail will not have this 2 nodes. head will point at node_next but will not have node_prev which point to it, and tail only have node_prev which will point to it).
so, if you want to create list you will have to dynamically allocate the space of the list which contain 2 nodes: head and tail(or end).
this is an example of create function:
slist_t *SlistCreate(void)
{
slist_t *list = malloc(sizeof(slist_t));
slist_iter_t dummy = NULL;
if(NULL == list)
{
return (NULL);
}
dummy = malloc(sizeof(node_t));
if(NULL == dummy)
{
free(list);
return (NULL);
}
dummy->next = NULL;
dummy->data = (void *)list;
list->head = dummy;
list->tail = dummy;
return (list);
}
then you will be able to insert nodes before particular node or after - that's depend on how you will implement this:
if you want to implement insert before:
you will have to encapsulate the list struct and prevent the user from sending your head to insert before.
or you can check every time if your insert function get the head of the list and update the head of the list (you can insert after and copy the head data to the head->next node and then use the head for the data the user wanted to be at the head of the list.
if you want to implement insert after:
you will have to check if the user sent you the tail (also called dummy_node cause it point to dummy_node->next = NULL), and use the same method i mentioned before, only the opposite way..
this is an example of insert before - in this example i used slist_iter_t which is pointer to node_t - the user isn't exposed to any of this structs (not to the list struct or the nodes struct):
slist_iter_t SlistInsert(slist_iter_t iterator, const void *data)
{
slist_t *ptr = NULL;
slist_iter_t dup = (slist_iter_t)malloc(sizeof(node_t));
if(NULL == dup)
{
return(NULL);
}
assert(NULL != iterator);
dup->data = iterator->data;
dup->next = iterator->next;
iterator->data = (void *)data;
iterator->next = dup;
if(NULL == dup->next)
{
ptr = (slist_t *)dup->data;
ptr->tail = dup;
}
return (iterator);
}
so, for using this DS you will have to write create function, insert function and destroy function (you will have to free all your dynamically allocated memory).
you may add more function such as remove, search data, clear list, is empty and so on. if you choose to encapsulate this implementation and hide from the user the struct of the list and the struct of the node, you will have to add more function such as get data function, get next node, and more..
you mentioned you need to insert if this data doesn't exist in the list so you can send from insert function to find function.
your function needs to look something like this:
struct job_list {
struct job_node * front;
struct job_list * next;
};
struct job_node {
int reference_number;
char * job_name;
struct job_node *next;
};
for your first function:
struct job_node *JobListCreate(void)
{
struct job_node *list = malloc(sizeof(struct job_node));
struct node_job dummy = NULL;
if(NULL == list)
{
return (NULL);
}
dummy = malloc(sizeof(node_t));
if(NULL == dummy)
{
free(list);
return (NULL);
}
dummy->next = NULL;
dummy->data = (void *)list;
list->head = dummy;
list->tail = dummy;
return (list);
}
for your second function:
void JobListInsertInFront(struct job_node *list, int reference_number, char * job_name)
{
slist_t *ptr = NULL;
struct node_job dup = NULL;
assert(NULL != list);
dup = (struct node_job)malloc(sizeof(node_t));
if(NULL == dup)
{
printf("Allocation failed\n");
return;
}
dup->reference_number = list->head->reference_number;
dup->job_name = list->head->job_name;
dup->next = list->head->next;
list->head->reference_number = reference_number;
list->head->job_name = job_name;
list->head->next = dup;
return;
}
and for the last function:
bool JobListInsertIfNotExist(struct job_node *list, int reference_number, char * job_name)
{
slist_t *ptr = NULL;
struct node_job dup = NULL;
assert(NULL != list);
while (NULL != dup)
{
if (dup->reference_number == reference_number && dup->job_name == job_name)
{
return false;
}
dup = dup->next;
}
dup = (struct node_job)malloc(sizeof(node_t));
if(NULL == dup)
{
printf("Allocation failed\n");
return;
}
dup->reference_number = list->head->reference_number;
dup->job_name = list->head->job_name;
dup->next = list->head->next;
list->head->reference_number = reference_number;
list->head->job_name = job_name;
list->head->next = dup;
return true;
}
As Jack Lilhammers pointed out in the comments, your code is very complex and there are a lot of mistakes in it, so I wrote down some general functions that you can then modify accordingly.
This is the basic struct, that we are going to work with:
struct node {
int data;
struct node *next;
};
Create a new node
Then this is how you'd create a new node:
#include <stdio.h>
#include <stdlib.h>
struct node *new_node(int data, struct node *next)
{
struct node *new = malloc(sizeof *new);
if (!new) {
printf("Error: memory allocation failed");
exit(EXIT_FAILURE);
}
new->data = data;
new->next = next;
return new;
}
You would initially call the function like this:
struct node *head = new_node(5, NULL);
Check if a node exists
Normally you would do something like this to check if a node with specific data exists in the linked list:
#include <stdbool.h>
/* Return whether or not the node exists */
bool node_exists(struct node *head, int data)
{
struct node *cursor = head;
while (cursor != NULL) {
if (cursor->data == data)
return true;
cursor = cursor->next;
}
return false;
}
Insert new node at the end
If you want to insert a new node at the end of the linked list, this is how it works:
void insert_last(struct node *head, struct node *new)
{
struct node **cursor = &head;
while ((*cursor) != NULL)
cursor = &(*cursor)->next;
*cursor = new;
}
Insert new node if nonexistent
You can combine the last two functions to only insert a new node at the end of the linked list, if the data doesn't already exist:
#include <stdbool.h>
/*
* Return whether or not the node exists. If it exists,
* insert the new node at the end of the linked list.
*/
bool new_insert_last(struct node *head, struct node *new)
{
struct node **cursor = &head;
while ((*cursor) != NULL) {
if ((*cursor)->data == new->data)
return true;
cursor = &(*cursor)->next;
}
*cursor = new;
return false;
}
This function could be called like this:
new_insert_last(head, new_node(3, NULL));
I have created a GitLab Snippet, so you can see the functions in action.
I'm having trouble writing a function that will delete a node on a generic linked list.
I have my linked list declared as follow (this is the way my professor wants us to do):
typedef enum _STATUS {ERROR,OK} STATUS;
typedef enum _BOOLEAN {FALSE, TRUE} BOOLEAN;
#define MAX_NOME 20
typedef struct _FUNC
{
char name[MAX_NOME];
char dept[MAX_NOME];
BOOLEAN permanent;
} FUNC;
typedef struct _LIST_NODE
{
void * data;
struct _LIST_NODE * next;
} LIST_NODE;
typedef LIST_NODE * LIST;
#define DATA(node) ((node)->data)
#define NEXT(node) ((node)->next)
I've come with this function to delete all nodes with permanent == FALSE, but it is really not working.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while ((*list)->next != NULL)
{
if(((FUNC*)DATA(*list))->permament == FALSE)
{
node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
}
else
{
list = NEXT(*list);
}
}
}
Any feedback would be greatly appreciated. Thank you.
You iterate through the list with a pointer to node pointer, which is a good idea. (Typecasting away the pointer nature of LIST is not a good idea, however.) There are several errors in yur code.
To get a pointer to the last element of a list, you do:
Node **p = &head;
while (*p) {
p = &(*p)->next;
}
Your respective code, i.e. your function without the deletion stuff, looks like this:
while ((*list)->next != NULL) {
list = NEXT(*list);
}
You should iterate while (*list). The idea to check next probably stems from similar code that uses a node pointer to iterate. When you use a pointer to node pointer, dereferencing that pointer has the same effect as accessing next, because that pointer points to the head node at first and to the previous node's next member on subsequent iterations.
That's why you must assign the address of (*list)->next to list when you want to advance the pointer. (The comiler warns you that the pointer types don't match.)
So the "raw" loop should be:
while (*list != NULL) {
list = &NEXT(*list);
}
Now let's look at deletion. When you have determined that the node should be deleted, you do:
LIST *node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
Here, you do not want to advance the iterator pointer. Instead, you want to update what it points at: You want to skip the current node *list by deflecting the pointer that points to the node to the next node or to NULL, when that was the last node:
*list = NEXT(*node);
When you do that, list and node will still be the same address, only the contents have changed. (Because node == list, *node now points at the node after the node you want to delete and you accidentally free that node and its data. Make the temporary pointer a simple node pointer:
LIST node = *list;
*list = NEXT(node);
free(DATA(node));
free(node);
Putting it all together:
void DeleteFuncNotPermanent(LIST *list, int c)
{
while (*list)
{
if (((FUNC*) DATA(*list))->permament == FALSE)
{
LIST node = *list;
*list = (NEXT(*list));
free(DATA(node));
free(node);
}
else
{
list = &NEXT(*list);
}
}
}
I have tried to rewrite your delete function. Let me know if it works. The changes are basically related to pointer dereferencing.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while (list!= NULL) // change
{
if(((FUNC*)(DATA(list)))->permament == FALSE) // change
{
node = list;
list = NEXT(node); //change
free((FUNC*)(DATA(node))); // change
free(node); // change
}
else
{
list = NEXT(list); //change
}
}
}
struct Link
{
frame_t *frame;
struct Link *next;
} typedef link_t;
Inside I allocate memory for frame_t
I tried recursively freeing it but the program crashed when I ran it.
struct Frame
{
char *name;
unsigned int duration;
char *path;
} typedef frame_t;
And inside of that I allocate memory for name and path.
void freeFrames(link_t** head)
{
if (*head != NULL)
{
if ((*head)->next != NULL)
{
free((*head)->frame->name);
free((*head)->frame->path);
free((*head)->frame);
freeFrames(&((*head)->next));
}
free(*head);
}
}
How can I free memory for this?
Have you initialised the pointers correctly? If you did not assign a concrete value to them, you must initialise them to NULL.
You cannot free a null pointer, so you should check: if(name) free(name);
You cannot free string literals! So if you do name = "hello", your program is guaranteed to fail if trying to do so.
Additionally, you have a memory leak (stole that one from n.m.'s comment...):
if ((*head)->next != NULL)
{
free((*head)->frame->name);
free((*head)->frame->path);
free((*head)->frame);
// ^ you will only free, if there is a successor node!
// so you won't free them on the last node!
freeFrames(&((*head)->next));
}
It should be (not having added the NULL checks so far!):
if(*head)
{
free((*head)->frame->name);
free((*head)->frame->path);
free((*head)->frame);
if ((*head)->next != NULL)
{
freeFrames(&((*head)->next));
}
free(*head);
}
Additionally: You do not assign anything to your head (*head = NULL;), so why do you have a double pointer?
void freeFrames(link_t* head);
would suffice...
And there is no need for recursion, you simply can advance the pointer as long as there is any:
void freeFrames(link_t* head)
{
while(head)
{
// free members, don't forget the NULL checks
link_t* tmp = head;
head = head->next;
free(tmp);
}
}
/*
* delete the first mode whose num match number in the list
*/
void delete(listnode *list, int num)
{
listnode *item, *tmp, *prev;
item = list;
while (item->next != NULL) {
if (item->next->num == num) {
tmp = item->next;
item->next = item->next->next;
} else {
item = item->next;
}
}
/*free(tmp);*/
}
so, if I comment free, it works well, else, it will just shot me a core dump, what should I do with the node which tmp point to?(note: assume we have a header, and the parameter list is a pointer to it)
There are several paths of execution which would lead to calling free on an uninitialized variable. initialize tmp to NULL.