How to call free() properly? - c

Kindly read the whole post since it includes small details which are highly important.
As known by C we should take care of incidents where malloc fails, for that case I created a function called destroyList() whose job is to take a pointer to Node and destroy it one by one.
But my function isn't being called correctly...
I tried to call it with ptr, merged_out and *merged_out (The last one was a suggestion from a member of the community) but nothing seems to work.
Why is that? the function sometimes receives NULL, Empty Lists or some random values.
Can someone please help me fix this issue and let me understand of what is going on?
typedef struct node_t {
int x;
struct node_t *next;
} *Node;
void destroyList(Node ptr) {
while (ptr) {
Node toDelete = ptr;
ptr = ptr->next;
free(toDelete);
}
}
Main Function:
ErrorCode mergeSortedLists(Node list1, Node list2, Node *merged_out) {
if (!list1 || !list2) {
return EMPTY_LIST;
}
if (!isListSorted(list1) || !isListSorted(list2)) {
return UNSORTED_LIST;
}
if (!merged_out) {
return NULL_ARGUMENT;
}
Node ptr = NULL;
int total_len = getListLength(list1) + getListLength(list2);
for (int i = 0; i < total_len; i++) {
int min = getMin(&list1, &list2);
ptr = malloc(sizeof(*ptr));
*merged_out = ptr;
if (!ptr) {
destroyList(*merged_out);
*merged_out = NULL;
return MEMORY_ERROR;
}
ptr->x = min;
ptr->next = NULL;
merged_out = &ptr->next;
}
return SUCCESS;
}
This is how the function should be called:
Node merged_actual = NULL;
ErrorCode merge_status = mergeSortedLists(list1, list2, &merged_actual);
Note: getMin() gets the minimum value and advances the pointer of the list which has that min value to the next node.

Start after those if checks.
Node ptr=NULL,last;
/* find out current tail of the list */
if (*merged_out!=NULL){
last=*merged_out;
while (last->next!=NULL){
last=last->next;
}
}
int total_len = getListLength(list1) + getListLength(list2);
for (int i = 0; i < total_len; i++)
{
int min = getMin(&list1, &list2);
ptr = malloc(sizeof(*ptr));
if (!ptr)
{
destroyList(*merged_out);
*merged_out=NULL;
return MEMORY_ERROR;
}
ptr->x = min;
ptr->next = NULL;
/* link ptr onto the list */
if (*merged_out==NULL){
/* if the list is empty, make ptr the head of the list */
*merged_out=ptr;
last=*merged_out;
}
else{
last->next = ptr;
last = ptr;
}
}
Please try not to copy and paste this block of code. It may or may not be correct, but try to understand what it did: iterate each time the function is called, in an effort to put last to point at the last element of the list. Therefore merged_out can always point to the head.

#user12986714 I lost my old account, and was told to not to care about the initial value of *merged_out could you update the solution (delete the first while loop and no need for 2 pointers)

Related

Why aren't new elements being added to my linked list?

This is just a snippet of the code, but I checked and know for a fact that all the strings save nicely into the "new" element (in function SortedInsert), but then the "new" doesn't link to the head?
I've tried everything I could think, hopefully I'm just missing something obvious.
typedef struct _Info* Position;
typedef struct _Info{
char name[MAX];
char surname[MAX];
Position next;
} Info;
(declaration inside main function:
Info headInfo = {.name = {0}, .surname {0}, .next = NULL};
Position head = &headInfo;
)
int SortedInsert(Position head, char name[], char surname[]){
Position prev = NULL, temp = NULL, new = NULL;
prev = head;
temp = head->next;
new = (Position)malloc(sizeof(Info));
if(!new){
return EXIT_FAILURE;
}
strcpy(new->name, name);
strcpy(new->surname, surname);
new->next = NULL;
if(head->next==NULL){
temp = new;
}
else{
// first sort, by surname
while(strcmp(temp->surname, new->surname) < 0){
prev = temp;
temp = temp->next;
}
// second sort, by name
while(strcmp(temp->name, new->name) < 0){
prev = temp;
temp = temp->next;
}
new->next = prev->next;
prev->next = new;
}
return EXIT_SUCCESS;
}
int PrintList(Position head){
Position temp = NULL;
temp = head->next;
while(temp){
printf("%s ", temp->name);
printf("%s\n", temp->surname);
printf("---\n");
temp = temp->next;
}
return EXIT_SUCCESS;
}
Some issues:
temp = new does not insert anything into the list. It merely copies a reference to the new node into a local variable. The assignment should be to head->next. Moreover, there is no need to create a separate case for this. It can be handled with the code you have in the else part.
The retrieval of the insert point is not correct. If in the first loop the strcmp call returns 1 (not 0), then the second while loop should not iterate at all: it doesn't matter in that case what the first name is like. The last name of temp is already greater, so the insertion point has been found. Similarly, if the strcmp call returns 0, the second loop should keep verifying that the last name is still the same in its second iteration,...etc. Moreover, this logic can be combined in one loop.
Not a problem for the correct execution, but still:
Many consider it bad practice to typedef a pointer to a struct where you dereference the pointer regularly in your code. See the answers to Is it a good idea to typedef pointers? for some background. So I'd keep using Info *.
Create a separate function for creating and initialising a node.
The comments that say "first sort", "second sort" are misleading. There is no sorting happening in the loop that follows the comment. The list is already sorted. The process that follows just intends to find the insertion spot according to the sort order. So the comment could be improved.
Many consider it better not to cast the value returned by malloc.
Here is the correction of the SortedInsert function, together with the separated function for node creation:
Info *createNode(char name[], char surname[]) {
Info *new = malloc(sizeof(*new));
if (new != NULL) {
strcpy(new->name, name);
strcpy(new->surname, surname);
new->next = NULL;
}
return new;
}
int SortedInsert(Info *head, char name[], char surname[]){
Info *new = createNode(name, surname);
if (new == NULL) {
return EXIT_FAILURE;
}
Info *prev = head;
Info *temp = head->next;
// Find insertion spot according to sort order
while (temp != NULL) {
int cmp = strcmp(temp->surname, new->surname);
if (cmp == 0) { // It's a tie. Then use name as discriminator
cmp = strcmp(temp->name, new->name);
}
if (cmp >= 0) { // Found insertion spot
break;
}
prev = temp;
temp = temp->next;
}
new->next = prev->next;
prev->next = new;
return EXIT_SUCCESS;
}

How to add new element to double linked list

I want to make a function that can add new elements to double linked list in C, but I couldn't do it. Here is code.
New element should have name, group etc. Just explain me how to make name and rest of it I will do by myself.
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <string.h>
typedef struct inform
{
char name[20];
char group[20];
char form[20];
int day;
int month;
int year;
int srok;
} INF_BLOK;
typedef struct list_elem
{
INF_BLOK inf;
struct list_elem *next, *prev;
} APTEKA;
APTEKA *head, *tail;
int InputData(INF_BLOK* inf);
int main()
{
return 0;
}
I tried to implement a function that inserts an element based on an given index. Note, that i changed your list_item struct a bit so that it contains a pointer to your data-elements.
Here is the implementation:
/**************************************************************
* Function: insert_index
* Parameters: APTEKA* head, INF_BLOK* data, int index
* Return value: Returns NULL on failure, a pointer to the head
* on success
* Description: Inserts a APTEKA* element based on an given
* index
***************************************************************/
APTEKA* insert_index(APTEKA* head, INF_BLOK* data, int index) {
// Local variable for index
int ind = 1;
APTEKA* new_node = (APTEKA*)malloc(sizeof(APTEKA));
new_node->inf = data;
// Check if head exists, the malloc call was successfull and the index is
// in allowed range
// NOTE: Index for head starts at position 1
if(head && new_node && index) {
// If index is one, set a new head
if(index == 1) {
// The previous node is of course NULL
new_node->prev = NULL;
new_node->next = head->next;
if(head->next)
head->next->prev = new_node;
head->next = new_node;
// In a full implementation you need to free the memory for head and the data field in the
// structure. free(...)
// Return a pointer to the new head of the list
return new_node;
} else {
APTEKA* current_node = head->next;
// Loop through all positions before the desired index
for(; ind < (index - 1); ++ind)
current_node = current_node->next;
new_node->prev = current_node;
new_node->next = current_node->next;
if(current_node->next)
current_node->next->prev = new_node;
current_node->next = new_node;
}
}
else {
// Return NULL on failure
return NULL;
}
// Return an pointer to the head
return head;
}
Explanation:
First the function creates a new node named new_node and sets the pointer of the inf data field to the given parameter. Before actually inserting i basically check for that everything is right.
I then divide into two cases: first one to replace the head (index == 1) and second one is for any other index.
If the head should be replaced i change the dependecies and return a pointer to the newly created node. For any other case i iterate to the element before the index and then try to insert it.
When i tested it with this main function, it seemed to work:
int main()
{
/* Only used for testing purposes */
APTEKA* head = (APTEKA*)malloc(sizeof(APTEKA));
APTEKA* first = (APTEKA*)malloc(sizeof(APTEKA));
APTEKA* tail = (APTEKA*)malloc(sizeof(APTEKA));
head->next = first, head->prev = NULL;
first->next = tail, first->prev = head;
tail->next = NULL, tail->prev = first;
/* Information for head node */
INF_BLOK* block_head = (INF_BLOK*)malloc(sizeof(INF_BLOK));
memcpy(block_head->name, "Head", 5);
/* Information for tail node */
INF_BLOK* block_tail = (INF_BLOK*)malloc(sizeof(INF_BLOK));
memcpy(block_tail->name, "Tail", 5);
/* Information for first block */
INF_BLOK* block_first = (INF_BLOK*)malloc(sizeof(INF_BLOK));
memcpy(block_first->name, "First", 6);
/* Information for block to add */
INF_BLOK* block_sec = (INF_BLOK*)malloc(sizeof(INF_BLOK));
memcpy(block_sec->name, "Second", 7);
head->inf = block_head, first->inf = block_first, tail->inf = block_tail;
if(!insert_index(head, block_sec, 2))
fprintf(stderr, "Error inserting element\n");
APTEKA* element = head;
/* Print out name-data of nodes */
while(element) {
puts(element->inf->name);
element = element->next;
}
element = head;
// Freeing everything
while (element) {
APTEKA* next = element->next;
free(element->inf), free(element);
element = next;
}
return 0;
}
Hopefully, my answer gives you the desired insights. If i did something wrong, please correct me :)
NOTE: For this answer i only used the name attribute of your data-item structure. For storing the group, form, etc. you will need another procedure setting those values.

Deleting a linked list node in a C function doesn't transfer to the calling function

I have this C function which is supposed to find an element in the linked list which has a specific "pos" value, delete it, and return the deleted value to the calling function. It does delete the item, but the change isn't saved in the calling function, the list just doesn't get updated with the new changes.
My list is structured like this:
struct list{
int value;
int pos;
struct list * next_ptr;
};
And my C function is this:
bool findDeleteElement(struct list **ptr, int position, int *value){
struct list** temp = ptr;
if(*ptr!=NULL){
while((*ptr)->pos!=position) ptr=&(*ptr)->next_ptr; //Gets to desired node
temp=ptr;
value=&(*ptr)->value; //saves the value
temp=&(*temp)->next_ptr; //Goes to next node
ptr=temp; //Makes ptr point to next node
return 1;
}
else return 0;
}
I just can't see what I'm missing.
I'm a beginner so I probably made a simple mistake.
Change to:
*value = (*ptr)->value; //saves the value
You only set value, the local copy of your external variable's address. This does not change your external variable in the calling function.
Some question:
What happens when position has the wrong value, such that no node is found?
What's the purpose of temp = ptr;, because temp is overwritten by temp = &(*temp)->next_ptr; without having been used.
Disclaimer: I've not further checked this function.
I kindly advise you to take on other code formatting rules that add more air and make things more readable. Here's an example:
bool findDeleteElement(struct list **ptr, int position, int *value)
{
struct list** temp = ptr;
if (*ptr != NULL)
{
// Gets to desired node
while((*ptr)->pos != position)
{
ptr = &(*ptr)->next_ptr;
}
temp = ptr;
*value = (*ptr)->value; // Saves the value
temp = &(*temp)->next_ptr; // Goes to next node
ptr = temp; // Makes ptr point to next node
return 1;
}
else
{
return 0;
}
}
You are confused about pointers and dereferencing and what & and * actually do. This is a normal state of affairs for a beginner.
To start with, ptr and value when used without * preceding them are function arguments and like automatic (local) variables they disappear when the function scope exits. So this statement:
value=&(*ptr)->value;
Merely changes the value of value i.e. what it points to and has no visible effect to the caller. What you need to change is the thing that value points to. i.e. the statement should look like this:
*value = (*ptr)->value;
The difference is that instead of setting value to the address of (*ptr)->value it sets what valuepoints to to (*ptr)->value.
You have a similar problem with ptr. But your problems are more subtle there because you are also trying to use it as a loop variable. It's better to separate the two uses. I'd write the function something like this:
bool findDeleteElement(struct list **head, int position, int *value)
{
struct list* temp = *head;
struct list* prev = NULL;
while(temp != NULL && temp->pos != position)
{
prev = temp;
temp = temp->next;
}
if (temp == NULL) // position not found
{
return false;
}
else
{
*value = temp->value;
// Now need to delete the node.
if (prev != NULL)
{
// If prev has been set, we are not at the head
prev->next = temp->next; // Unlink the node from the list
}
else // We found the node at the head of the list
{
*head = temp->next;
}
free(temp); // Assumes the node was malloced.
return true;
}
}
The above is not tested or even compiled. I leave that as an exercise for you.
int delete(struct llist **pp, int pos, int *result)
{
struct llist *tmp;
while ( (tmp = *pp)) {
if (tmp->pos != pos) { pp = &tmp->next; continue; }
*result = val;
*pp = tmp->next;
free(tmp);
return 1;
}
return 0;
}

Pointers to pointers - linked list mess

I'm writing a simple C program to manage a linked list defined as follow:
typedef struct node {
int value;
struct node *next;
} *List;
I reviewed the code and it seems okay but when printing results something is not working well.
My main, with problems on comments:
int main(void) {
List n = list_create(1);
insert(n, 2);
insert(n, 3);
insert(n, 5);
insert(n, 4);
//something here does not work properly. It produces the following output:
//Value: 1
//Value: 2
//Value: 3
//Value: 4
//where is value 5?
print_list(n);
delete(n, 3);
print_list(n);
return 0;
}
I don't know where am I destroying list structure. These are my functions, to debug, if you are too kind.
List list_create(int value) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = NULL;
return new;
}
List new_node(int value, List next_node) {
List new = malloc(sizeof(struct node));
new->value = value;
new->next = next_node;
return new;
}
void print_list(List l) {
List *aux;
for (aux = &l; (*aux) != NULL; aux = &((*aux)->next))
printf("Valor: %d\n", (*aux)->value);
}
void insert(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value > value) {
List tmp = *p;
List new = new_node(value, tmp);
*p = new;
break;
}
*p = new_node(value, NULL);
}
void delete(List l, int value) {
List *p;
for (p = &l; (*p) != NULL; p = &((*p)->next))
if ((*p)->value == value) {
List del = (*p);
(*p) = ((*p)->next);
free(del);
break;
}
}
This code has (at least) two bugs:
The line
if ((*p)->value > value){
means that if you start the list with 1 as the first value and then try to insert 2,3,4..., the body of the 'if' statement never runs, so nothing ever gets inserted.
If you insert a value below the starting value, you have to modify the list pointer itself. However, as #EOF alluded, you are trying to modify a value passed to a function by taking its address. This won't work. &l does not give you the address of the List you passed, it gives you the address of the local copy on insert()'s stack. You are better off modifying the values of first element of the list 'in place'. If you really want to make the List parameter mutable, you'll need to pass it as a List *, and call the function with the address of the list (e.g. insert(&n,2); ) Your delete() function suffers from the same problem - try deleting the first element of the list.
Try this for your insert function:
void insert(List l, int value)
{
List p;
// Find end of list or highest item less than value
for(p = l; p->next != NULL && p->next->value < value; p = p->next);
if (p->value >= value) {
// Over-write p with new value, and insert p as a new one after.
// This saves having to modify l itself.
int tmpval = p->value;
p->value = value;
p->next = new_node(tmpval, p->next);
} else {
// Insert new item after p
p->next = new_node(value, p->next);
}
}
A comment: it is possible the way you are using pointers is not helping the debugging process.
For example, your print_list() could be re-written like this:
void print_list(List l){
List aux;
for(aux = l; aux != NULL; aux = aux->next)
printf("Valor: %d\n", aux->value);
}
and still behave the same. It is generally good practice not to 'hide' the pointer-like nature of a pointer by including a '*' in the typedef.
For example, if you define your list like this:
typedef struct node{
int value;
struct node *next;
} List
And pass it to functions like this:
my_func(List *l, ...)
then it'll make some of these issues more apparent. Hope this helps.
There are many problems in your code:
Hiding pointers behind typedefs is a bad idea, it leads to confusion for both the programmer and the reader.
You must decide whether the initial node is a dummy node or if the empty list is simply a NULL pointer. The latter is much simpler to handle but you must pass the address of the head node to insert and delete so they can change the head node.
printlist does not need an indirect pointer, especially starting from the address of the pointer passed as an argument. Simplify by using the Node pointer directly.
in insert you correctly insert the new node before the next higher node but you should then return from the function. Instead, you break out of the switch and the code for appending is executed, replacing the inserted node with a new node with the same value and a NULL next pointer. This is the reason 5 gets removed and lost when you insert 4. Furthermore, you should pass the address of the head node so a node can be inserted before the first.
delete starts from the address of the argument. It cannot delete the head node because the pointer in the caller space does not get updated. You should pass the address of the head node.
You should avoid using C++ keywords such as new and delete in C code: while not illegal, it confuses readers used to C++, confuses the syntax highlighter and prevents compilation by C++ compilers.
Here is a simplified and corrected version:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int value;
struct Node *next;
} Node;
Node *new_node(int value, Node *next_node) {
Node *node = malloc(sizeof(*node));
if (node != NULL) {
node->value = value;
node->next = next_node;
}
return node;
}
void print_list(Node *list) {
for (; list != NULL; list = list->next)
printf("Valor: %d\n", list->value);
}
void insert_node(Node **p, int value) {
while ((*p) != NULL && (*p)->value < value)
p = &(*p)->next;
*p = new_node(value, *p);
}
void delete_node(Node **p, int value) {
while (*p != NULL) {
if ((*p)->value == value) {
Node *found = *p;
*p = (*p)->next;
free(found);
// return unless delete() is supposed to remove all occurrences
return;
} else {
p = &(*p)->next;
}
}
}
int main(void) {
Node *n = NULL;
insert_node(&n, 2);
insert_node(&n, 3);
insert_node(&n, 5);
insert_node(&n, 4);
insert_node(&n, 1);
print_list(n);
delete_node(&n, 3);
print_list(n);
delete_node(&n, 1);
print_list(n);
return 0;
}

Can't free linked list

This is the struct:
typedef struct listeEle {
int pos;
struct listeEle *next;
} ListEle;
this is where I create the list:
ListEle *mokli(int n){
if(n<=0)
{
fprintf(stderr, "Falscher Parameter für mokli... Programm beendet.");
exit(0);
}
else
{
ListEle *lst;
lst = malloc(sizeof(ListEle));
lst->next = NULL;
lst->pos = 1;
int i;
ListEle *new;
ListEle *ptr;
ptr = lst;
for(i=1; i<n; i++)
{
new = NULL;
new = malloc(sizeof(ListEle));
new->next = NULL;
new->pos = i+1;
ptr->next = new;
ptr = ptr->next;
}
return lst;
}
}
and this is where I try to free it
void unmokli(ListEle *lst)
{
if(lst->next == NULL)
{
free(lst);
lst = NULL;
printList(lst);
printf("1 > Liste vollständig gelöscht.\n");
}
else
{
ListEle *head;
head = lst;
int del = 0;
while(head)
{
ListEle *temp = head;
head = head->next;
free(temp);
del+=1;
}
free(lst);
lst = NULL;
printf("2 > Liste deleted (%d).\n", del);
}
}
This is how I print it
void printList(ListEle *anfang){
if(anfang == NULL)
{
printf("List not av...\n");
}
else
{
ListEle *ptr;
ptr = anfang;
int i = 1;
while(ptr)
{
printf("Element %i -> ListeEle pos=%i\n", i++, ptr->pos);
ptr = ptr->next;
}
}
}
The problem is in the main method. The list I create here won't be freed, although I pass it correctly to the freeing function.
int main(void){
ListEle *liste;
liste = mokli(6);
printList(liste);
unmokli(liste);
printList(liste);
return 0;
}
After unmokli it should be freed completely, but the list inside the main function is still allocated. How can I completely free the list?
When you do
lst = NULL;
in the function unmokli you are only making the local copy lst NULL. Remember that arguments are passed by value, i.e. they are copied. You need to pass lst by reference, which can be done by passing a pointer (to the pointer):
void unmokli(ListEle **lst)
{
...
*lst = NULL;
...
}
You then call this by using the address-of operator:
unmokli(&liste);
Either pass reference of liste to unmokli() or take return parameter from it as you have done in mokli().
In your case, its getting freed, but variable liste in main() is not set to NULL and still references old memory. This is may cause you crash.
Update your code as
void unmokli(ListEle **lst_ref)
{
ListEle *lst = *lst_ref;
...
//your code freeing list
//set to null after freeing
*lst_ref = NULL;
}
From main() call it as
unmokli(&liste);
It's freed, but data still remains after calling free, so you just accessing deleted data by printing list after delete. (which you mustn't do actually)
Also, if you want to set 'liste' pointer to NULL inside free-function, you should pass pointer to a pointer. The following code in unmokli is useless:
free(lst); // Already freed in a loop before
lst = NULL; // Takes effect only inside this function
Typically, I would free (& NULL assign) the linked list as below:
void unmokli(ListEle **lst)
{
ListEle* nxt;
if(lst==NULL) return;
// printList(*lst); // not required by the free-list logic as such...
while(*lst)
{
nxt=(*lst)->next;
// printList(*lst); // not required by the free-list logic as such... You can test free code with this.
free(*lst);
*lst=nxt;
}
}
//usage
unmokli(&list_head);

Resources