I'm making a generic map in c (using clion) and in my nodeDestroy function I call a generic freeing function to free the key and data of the node list.
nodeDestroy:
void nodeDestroy(Node node) {
Node current = node;
Node next;
while (current != NULL) {
next = current->next;
current->free_key(current->key);
current->key = NULL;
current->free_data(current->data);
current->data = NULL;
free(current);
current = next;
}
node = NULL;
}
this is the node struct:
struct node_t{
MapKeyElement key;
MapDataElement data;
Node next;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
};
I am testing the map with integer pointers (the data and key of each node are int*) and this is the integer freeing function:
static void freeInt(MapKeyElement e) {
free((int*)e);
}
free_key and free_data are given to me in nodeCreate and both are set to freeInt. When I check this in valgrind the key and data of each node aren't freed. any idea?
So I noticed the problem occurs only when using another function: mapPut. I'll add implementation of functions required to understand the code and some things I didn't add before.
/** Data element data type for map container */
typedef void* MapDataElement;
/** Key element data type for map container */
typedef void* MapKeyElement;
/** Type of function for copying a data element of the map */
typedef MapDataElement(*copyMapDataElements)(MapDataElement);
/** Type of function for copying a key element of the map */
typedef MapKeyElement(*copyMapKeyElements)(MapKeyElement);
/** Type of function for deallocating a data element of the map */
typedef void(*freeMapDataElements)(MapDataElement);
/** Type of function for deallocating a key element of the map */
typedef void(*freeMapKeyElements)(MapKeyElement);
struct Map_t{
Node iterator;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
compareMapKeyElements compare_keys;
Node head;
};
bool mapContains(Map map, MapKeyElement element){
if (map == NULL || element == NULL){
return false;
}
mapGetFirst(map);
if (getKey(map->iterator) != NULL && map->compare_keys(getKey(map->iterator)
, element) == 0){
return true;
}
while (mapGetNext(map) != NULL){
if (getKey(map->iterator) != NULL &&
map->compare_keys(getKey(map->iterator), element) == 0){
return true;
}
}
return false;
}
MapResult mapPut(Map map, MapKeyElement keyElement, MapDataElement dataElement){
if (map == NULL || keyElement == NULL || dataElement == NULL){
return MAP_NULL_ARGUMENT;
}
MapDataElement data_copy = map->copy_data(dataElement);
if (data_copy == NULL){
return MAP_OUT_OF_MEMORY;
}
MapResult result = MAP_SUCCESS;
if (mapContains(map,keyElement) == true){
mapGetFirst(map);
while (map->iterator != NULL){
if (map->compare_keys(keyElement, getKey(map->iterator)) == 0) {
map->free_data(getData(map->iterator));
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
map->iterator = getNext(map->iterator);
}
}
mapGetFirst(map);
MapKeyElement key_copy = map->copy_key(keyElement);
if (key_copy == NULL){
map->free_data(data_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
if (getKey(map->iterator) == NULL){
setKey(map->iterator, key_copy);
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
Node new_node = nodeCreate(map->copy_data, map->copy_key,
map->free_data, map->free_key, &result);
if (result == MAP_OUT_OF_MEMORY) {
map->free_data(data_copy);
map->free_key(key_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
setData(new_node, data_copy);
setKey(new_node, key_copy);
if (map->compare_keys(key_copy, getKey(map->iterator)) < 0){
setNext(new_node,map->iterator);
map->head = new_node;
return setIterator(map, MAP_SUCCESS);
}
while(map->iterator != NULL) {
if (getNext(map->iterator) == NULL){
setNext(map->iterator, new_node);
break;
}
if (map->compare_keys(key_copy, getKey(getNext(map->iterator))) < 0){
Node temp_next = getNext(map->iterator);
setNext(map->iterator, new_node);
setNext(new_node, temp_next);
break;
}
map->iterator = getNext(map->iterator);
}
return setIterator(map, MAP_SUCCESS);
}
static MapResult setIterator(Map map, MapResult result){
map->iterator = NULL;
return result;
}
MapKeyElement mapGetFirst(Map map){
if (map == NULL){
return NULL;
}
map->iterator = map->head;
return getKey(map->head);
}
Related
int add_after(ITEM *list, ITEM *c_item, int value)
{
//create a new node
ITEM *elem = malloc(sizeof(ITEM));
elem->value = value;
//if head is NULL, it is an empty list
if (elem == NULL || c_item == NULL || list == NULL) {
return -1;
} else {
while (list != NULL) {
if (list == c_item) {
elem->next = list->next;
elem->value = value;
list->next = elem;
list = list->next;
}
list = list->next;
}
}
return 0;
}
I am trying to make a function the puts a node after an existing node. my problem is inside the else statement. I am trying to find a node list equal to node c_item. After finding the equal, it should enter elem in between them.
I would rewrite your function as follow:
int add_after(ITEM *list, ITEM *c_item, int value)
{
//create a new node
ITEM *elem;
//if head is NULL, it is an empty list
if (!list || !c_item) {
return -1;
}
/* Create a new node. */
elem = malloc(sizeof(ITEM));
if (!elem) {
return -1;
}
/* You should rather set the value after having checked the allocated
* memory is not NULL. */
elem->value = value;
/* Insert elem ahead of the current item. */
elem->next = c_item->next;
c_item->next = elem;
return 0;
}
If you wish to insert a new node after a given node, you do not need to iterate on the list, just insert the new node as the next node of the c_item node:
int add_after(ITEM *list, ITEM *c_item, int value) {
//check function arguments
if (list == NULL || c_item == NULL)
return -1;
//create a new node
ITEM *elem = malloc(sizeof(ITEM));
if (elem == NULL)
return -1;
elem->value = value;
elem->next = c_item->next;
c_item->next = elem;
return 0;
}
In your approach, you try and locate the element in the list, which might be necessary if the caller cannot be trusted. In this case, you should have a different return value whether the element was inserted or not and free the allocated elem if c_item was not found:
int add_after(ITEM *list, ITEM *c_item, int value) {
//create a new node
ITEM *elem = malloc(sizeof(ITEM));
//if head is NULL, it is an empty list
if (elem == NULL || c_item == NULL || list == NULL) {
return -1;
} else {
while (list != NULL) {
if (list == c_item) {
elem->value = value;
elem->next = list->next;
list->next = elem;
return 1; // element was inserted
}
list = list->next;
}
free(elem);
return 0; // c_item was not found, elem not inserted
}
}
map contains - returns whether or not a key exists inside the map.
mapPut - Gives a specific key a given value and adding it to the map by order, if the key exists, the value is overridden.
mapRemove - Removes a pair of (key, data) elements for which the key matches a given element (by the key compare function).
mapGetFirst - Sets the internal iterator to the first key in the map, and returns it.
MapKeyElement mapGetFirst(Map map){
if(map == NULL){
return NULL;
}
if (map->head == NULL){
return NULL;
}
map->iterator = map->head;
return (map->copyMapKeyElements(map->iterator->key));
}
mapGetNext - Advances the internal iterator to the next key and
returns it.
MapKeyElement mapGetNext(Map map){
if(map == NULL){
return NULL;
}
if((map->iterator->next)== NULL) {
return NULL;
}
map->iterator = map->iterator->next;
return (map->copyMapKeyElements(map->iterator->key));
}
typedef struct MapElements_t{
MapDataElement data;
MapKeyElement key;
struct MapElements_t* next;
} *MapElements;
struct Map_t{
copyMapDataElements copyMapDataElements;
copyMapKeyElements copyMapKeyElements;
freeMapDataElements freeMapDataElements;
freeMapKeyElements freeMapKeyElements;
compareMapKeyElements compareMapKeyElements;
MapElements head;
MapElements iterator;
};
/* ...... */
MapResult mapPut(Map map, MapKeyElement keyElement, MapDataElement dataElement) {
if ((map == NULL) || (keyElement == NULL) || (dataElement == NULL)) {
return MAP_NULL_ARGUMENT;
}
if (mapContains(map, keyElement)) {
mapRemove(map, keyElement);
}
MapElements new_map_element = malloc(sizeof(new_map_element));
if (new_map_element == NULL) {
return MAP_OUT_OF_MEMORY;
}
new_map_element->data = dataElement;
new_map_element->key = keyElement;
new_map_element->next = NULL;
if(map->head == NULL){
map->head = new_map_element;
map->iterator = map->head;
return MAP_SUCCESS;
}
mapGetFirst(map);
if (map->compareMapKeyElements(keyElement, map->iterator->key) < 0){
new_map_element->next = map->iterator;
map->head = new_map_element;
return MAP_SUCCESS;
}
while(map->iterator->next != NULL) {
if (map->compareMapKeyElements(keyElement, map->iterator->next->key) < 0) {
new_map_element->next = map->iterator->next;
map->iterator = new_map_element;
return MAP_SUCCESS;
}
mapGetNext(map);
}
map->iterator->next = new_map_element;
return MAP_SUCCESS;
}
You have typedefs that include a pointer, such as typedef struct MapElements_t{...} *MapElements; which makes the type MapElements a pointer.
This is discouraged and for the following reason:
When you do
MapElements new_map_element = malloc(sizeof(new_map_element));
you are allocating the size of a pointer, not the size of the thing pointed to. In your case you should do:
MapElements new_map_element = malloc(sizeof(*new_map_element));
but preferably you would do:
typedef struct MapElements_t
{
//...
struct MapElements_t* next;
} MapElements;
so you make a variable that is a pointer to the thing have explicitly the *.
MapElements *new_map_element = malloc(sizeof(*new_map_element));
The fault was here; I have to replace this code:
new_map_element->data = dataElement;
new_map_element->key = keyElement;
with this code:
new_map_element->data = map->copyMapDataElements(dataElement);
new_map_element->key = map->copyMapKeyElements(keyElement);
I am using a struct like this
struct infoM {
char* direction;
int key;
};
typedef struct nodeM{
struct infoM nodeInfo;
struct nodeM *next;
struct nodeM *prev;
} node;
typedef node list;
I have one function that returns the wanted node by a specific field
node * search(list *l, char* direction) {}
And this is my function to remove elements from the list
int delete(list *l, char* direction) {
node *tmp = search(l, direction);
if (tmp != NULL) {
node *ant = tmp->prev;
node *seg = tmp->next;
if (seg != NULL) {
if (ant != NULL) {
ant->next = seg;
seg->prev = ant;
free(tmp);
return 1;
} else { //prev null
seg->prev = NULL;
*l = *seg;
tmp = NULL;
free(tmp);
return 1;
}
} else { //next null
if (ant == NULL) {
l->nodeInfo.key = somevalue;
l->next = NULL;
l->prev = NULL;
return 1;
} else {
printf("Here is the problem\n");
ant->next = NULL;
free(tmp);
return 1;
}
}
} else { //tmp nulo
perror("Error delete : node null\n");
return 0;
}
}
If I have 4 elements in the list, 1234 and I delete first first element everything is okay and returns 234. If I delete the last element it returns 23 seems to work great. But if I try to delete the last element now the function does nothing despite being the same case that when it is 234 and I don't understand why. The list is not being updated.
In the main I am using the list like this :
list a;
delete(&a, "whatever");
What am I doing wrong ?
This is the code for search
node * createnode(){
node *tmp = (node *) malloc (sizeof(node));
return tmp;
}
node * search(list *l, char* direction) {
node *tmp = createnode();
if (l->nodeInfo.key == 777) {
perror("Error search: empty list\n");
return NULL;
}
tmp=l;
while((strcmp(direction, tmp->nodeInfo.direction) !=0) && (tmp->next != NULL)) {
tmp = tmp->next;
}
if (strcmp(direction, tmp->nodeInfo.direction) == 0) {
return tmp;
} else {
perror("Error search: element not found\n");
return NULL;
}
}
I am trying to return the data in the node after freeing the node. But I receive segfault when I am trying to do that (my guess is that when I assign the elem value to the variable data, it only copies the reference. After the memory location of the node freed, we lose the member elem inside the node struct. Therefore it is illegal to retrieve the elem via its reference.). Is there any other way of doing it?
void* remove_node(struct s_node** node){
if(node != NULL && *node!= NULL && (*node)->elem != NULL){
void* data = (*node)->elem;
if((*node)->prev == NULL && (*node)->next == NULL){
free(*node);
}else if((*node)->prev == NULL){
(*node)->next->prev = NULL;
(*node)->next = NULL;
free(*node);
}else if((*node)->next == NULL){
(*node)->prev->next = NULL;
(*node)->prev = NULL;
free(*node);
}else{
(*node)->prev->next = (*node)->next;
(*node)->next->prev = (*node)->prev;
(*node)->prev = NULL;
(*node)->next = NULL;
free(*node);
}
return data;
}else{
return NULL;
}
}
And here is the struct
struct s_node {
void* elem;
struct s_node* next;
struct s_node* prev;
};
The code posted does not seem to be causing the bug you observe. The tree structure might get corrupted somewhere else.
Note however that it can be simplified drastically:
void *remove_node(struct s_node **node) {
if (node != NULL && *node != NULL && (*node)->elem != NULL) {
void *data = (*node)->elem;
if ((*node)->prev != NULL) (*node)->prev->next = (*node)->next;
if ((*node)->next != NULL) (*node)->next->prev = (*node)->prev;
free(*node);
*node = NULL; /* safely, cannot hurt */
return data;
} else {
return NULL;
}
}
My C/C++ linked-list remove function does not remove an element from the list. Here follows some of my code;
struct listIntElement {
struct listIntElement *next;
int data;
};
typedef struct listIntElement ListIntElement;
ListIntElement *head = NULL;
/*
Inserts a new element infront of the list.
*/
bool insert(ListIntElement **head, int data) {
// Allocate memory for new element. The cast is needed here as we are using a C++ compiler.
ListIntElement *newElement = (ListIntElement *) malloc(sizeof(ListIntElement));
// Check if memory allocation was succesfull.
if (newElement == NULL)
return false;
// Set the data for the new element of the list.
newElement->data = data;
// Keep track of the new head of the list.
newElement->next = *head;
*head = newElement;
return true;
}
/*
Deleting an element in the list.
*/
bool remove(ListIntElement **head, ListIntElement *elementToDelete) {
ListIntElement *element = *head;
// Check for NULL pointers.
if (head == NULL || *head == NULL || elementToDelete == NULL)
return false;
// Special case for the head.
if (elementToDelete == *head) {
*head = element->next;
free(elementToDelete);
return true;
}
// Traversal of the list to find the element to remove.
while (element != NULL) {
if (element->next == elementToDelete) {
// Relink the list so that it does not include the element to be deleted.
element->next = elementToDelete->next;
free(elementToDelete);
return true;
}
element = element->next;
}
// elementToDelete was not found.
return false;
}
/*
Finding an element in the list.
*/
ListIntElement find(ListIntElement **head, int data) {
// Take care of the head as we don't want to use the head
// in the traversal operation.
ListIntElement *element = *head;
while (element != NULL && element->data != data) {
element = element->next;
}
return *element;
}
/*
Displaying the list.
*/
void displayList(ListIntElement **head) {
ListIntElement *element = *head;
// Check if list is empty.
if (head == NULL | *head == NULL) {
printf("List is empty\n");
}
while (element != NULL) {
printf("%d --> ", element->data);
element = element->next;
}
printf("NULL");
printf("\n");
}
Here is my test code;
/*
* Testing a linked list.
*/
ListIntElement found;
printf("Linked list test\n");
insert(&head,0);
insert(&head, 1);
insert(&head, 2);
insert(&head, 3);
insert(&head, 4);
insert(&head, 5);
displayList(&head);
printf("size is: %d\n", size(&head));
found = find(&head, 5);
printf("This was found: %d\n", found.data);
remove(&head,&found);
displayList(&head);
I have found this section to be the section where things go wrong in the remove function;
// Special case for the head.
if (elementToDelete == *head) {
*head = element->next;
free(elementToDelete);
return true;
}
Take notice that I am using MS Visual Studio 2015 to write C code and using a C++ compiler.
From find function, you were returning a copy, and not the address itself, so your changes were not reflected in the calling function. Fixed it.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define true 1
#define false 0
struct listIntElement {
struct listIntElement *next;
int data;
};
typedef struct listIntElement ListIntElement;
ListIntElement *head = NULL;
/*
Inserts a new element infront of the list.
*/
bool insert(ListIntElement **head, int data) {
// Allocate memory for new element. The cast is needed here as we are using a C++ compiler.
ListIntElement *newElement = (ListIntElement *) malloc(sizeof(ListIntElement));
// Check if memory allocation was succesfull.
if (newElement == NULL)
return false;
// Set the data for the new element of the list.
newElement->data = data;
// Keep track of the new head of the list.
newElement->next = *head;
*head = newElement;
return true;
}
/*
Deleting an element in the list.
*/
bool removeElement (ListIntElement **head, ListIntElement *elementToDelete) {
ListIntElement *element = *head;
// Check for NULL pointers.
if (head == NULL || *head == NULL || elementToDelete == NULL)
return false;
// Special case for the head.
if (elementToDelete == *head) {
*head = element->next;
free(elementToDelete);
return true;
}
// Traversal of the list to find the element to remove.
while (element != NULL) {
if (element->next == elementToDelete) {
// Relink the list so that it does not include the element to be deleted.
element->next = elementToDelete->next;
free(elementToDelete);
return true;
}
element = element->next;
}
// elementToDelete was not found.
return false;
}
/*
Finding an element in the list.
*/
ListIntElement *find(ListIntElement **head, int data) {
// Take care of the head as we don't want to use the head
// in the traversal operation.
ListIntElement *element = *head;
while (element != NULL && element->data != data) {
element = element->next;
}
return element;
}
/*
Displaying the list.
*/
void displayList(ListIntElement **head) {
ListIntElement *element = *head;
// Check if list is empty.
if (head == NULL | *head == NULL) {
printf("List is empty\n");
}
while (element != NULL) {
printf("%d --> ", element->data);
element = element->next;
}
printf("NULL");
printf("\n");
}
int main () {
ListIntElement *found;
printf("Linked list test\n");
insert(&head,0);
insert(&head, 1);
insert(&head, 2);
insert(&head, 3);
insert(&head, 4);
insert(&head, 5);
displayList(&head);
printf("size is: %d\n", sizeof(&head));
found = find(&head, 5);
printf("This was found: %d\n", found->data);
removeElement(&head,found);
displayList(&head);
}
Output:
Linked list test
5 --> 4 --> 3 --> 2 --> 1 --> 0 --> NULL
size is: 4
This was found: 5
4 --> 3 --> 2 --> 1 --> 0 --> NULL