why the head at main doest get the node? - c

im trying to insert node to the begging of link list but the value from poiter to poiter isnt passing
i wrote a note ***** where the problem accure
void insertTolist(list* head, list* node)
{
list* current = head;
list* currentPlusOne = head->next;
while (current->next != NULL)
{
if (current->data<node->data && currentPlusOne->data>node->data)
{
current->next = node;
node->next = currentPlusOne;
break;
}
if (current->data<node->data && currentPlusOne->next == NULL)
{
current->next = node;
node->next = (list*)calloc(1, sizeof(list));
break;
}
if (current->data > node->data && currentPlusOne->data >node->data)// b c
{
node->next =current;
head = node;// ***the head doesnt chanching at the main***
break;
}
current = current->next;
currentPlusOne = currentPlusOne->next;
}
//printlist(head);
}

The function declared like
void insertTolist(list* head, list* node)
deals with a copy of the value of the pointer to the head node used as an argument. Changing the copy in this statement
head = node;
is not reflected on the value of the original pointer.
Moreover the function can invoke undefined behavior if the passed pointer is a null pointer at least due to this declaration
list* currentPlusOne = head->next;
Also this statement
node->next = (list*)calloc(1, sizeof(list));
does not make a sense.
You need either to pass the pointer to the head node to the function by reference through a pointer to it or to return from the function the pointer (possibly modified) to the head node and to assign its value to the original pointer.
If to use the first approach then the function will look enough simple.
void insertTolist( list **head, list *node )
{
while ( *head != NULL && !( node->data < ( *head )->data ) )
{
head = &( *head )->next;
}
node->next = *head;
*head = node;
}
and if in the caller the pointer head is declared like
list *head = NULL;
then the function is called like
insertTolist( &head, node );
where node is a pointer to the inserted node in the list.

Related

Program throws heap use after free error when I perform certain test cases

When I run the program it works except when I delete a fairly large number such as 100 or above, whenever I input anything remotely as large as that number I get the Heap use after free error. The terminal is saying it is caused by line 53 in insert() which is this line, tail->next = newNode;. I know keeping head and tail pointers as global variables are not the best way to write it, but I will change it once I get this to work.
void insert(int nData) {
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node)+10);
newNode->data = nData;
newNode->next = NULL;
if(checkDuplicates(nData)==1){
return;
}else{
if(head == NULL){
head = newNode;
tail = newNode;
}
else {
tail->next = newNode;
tail = newNode;
}
}
}
void delete(int n){
if(head -> data==n){
struct Node *tempHead = head;
head= head -> next;
free(tempHead);
return;
} else{
struct Node *current = head;
struct Node *prev = NULL;
while(current!=NULL&&current->data!=n){
prev = current;
current = current -> next;
}
if(current==NULL) return;
prev->next = current->next;
free(current);
}
}
There is a possible case where tail->next = newNode would be executed on a freed node: that happens when earlier on you called delete and that deleted the tail node in the list. In that case your code does not adjust the value of tail.
So in delete change:
head = head -> next;
to:
head = head -> next;
if (head == NULL) {
tail == NULL;
}
And in the else block change:
prev->next = current->next;
free(current);
to:
prev->next = current->next;
if (tail == current) {
tail = prev;
}
free(current);
For starters the function insert can produce a memory leak because at first a memory is allocated
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node)+10);
(moreover it is unclear what the magic number 10 means in this expression sizeof(struct Node)+10). And then if the condition of the if statement
if(checkDuplicates(nData)==1){
return;
evaluates to logical true the function exits without freeing the allocated memory.
You need at first to check whether the value is not already present in the list and only after that to allocate memory for the new node.
As for the reason of the error then it seems the reason of the error is inside the function delete. The function does not reset the pointer tail when a single node is present in the list or when the node pointed to by the pointer tail is removed.
And apart from this even the first statement of the function
if(head -> data==n){
can invoke undefined behavior when the function is called for an empty list.
The function should be defined the following way
int delete( int n )
{
struct Node *current = head;
struct Node *prev = NULL;
while ( current != NULL && current->data != n )
{
prev = current;
current = current->next;
}
int success = current != NULL;
if ( success )
{
if ( prev == NULL )
{
head = head->next;
if ( head == NULL ) tail = NULL;
}
else
{
prev->next = current->next;
if ( prev->next == NULL ) tail = prev;
}
free( current );
}
return success;
}

Trying to remove last element from linked list and save it into a variable passed as parameter giving segfault

I have a linked list in which I am trying to remove the last element that is a char* and set the parameter I pass into that function to the char*. However, this either gives a segfault or prints out gibberish. Why is this happening?
Here is the linked list:
struct list_node {
char* name;
struct list_node* next;
};
typedef struct list_node node;
And here are its functions (I'm pretty sure the other functions work but not the remove_last):
int remove_last(node* head, char* ret) {
while(head) {
if (head->next->next == NULL) {
printf("here\n");
ret = strdup(head->next->name);
printf("%s\n", ret);
printf("there\n");
//free(head->next);
//head->next = NULL;
return 0;
}
head = head->next;
}
return -1;
}
void add_last(char* name, node* current) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
while(current) {
if (current->next == NULL) {
current->next = new_node;
new_node->name = name;
return;
}
current = current->next;
}
}
node* add_first(char* name, node* head) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
new_node->name = name;
new_node->next = head;
head = new_node;
return head;
}
Inside the function, when I printf ret, I get what I am supposed to get, but in my main when I try to print out the ret variable I pass into parameters:
char *temp = NULL;
remove_last(head, temp);
printf("%s\n", temp);
I get a segfault. I originally thought maybe it was because I was setting the nodes and freeing them, but I also used strdup (which I think copies it to a new location or smth like that?). I think it might also have something to do with how I am setting temp to null and then I am not setting assigning ret = name correctly in the function? Any advice?
For starters the function add_last is incorrect and can invoke undefined behavior.
Firstly it does not check whether the pointer current (I suppose it can be a pointer to the head node) is equal to NULL. In this case there is a memory leak because the newly created node is not appended to the list.
Secondly the function does not set the data member next of the newly created node to NULL
At least the function can be declared and defined the following way if to use your approach to implementing the function add_first
node * add_last(char* name, node *head ) {
node* new_node;
new_node = (node*)malloc(sizeof(node));
new_node->name = name;
new_node->next = NULL;
if ( head == NULL )
{
head = new_node;
}
else
{
current = head;
while ( current->next ) current = current->next;
current->next = new_node;
}
return head;
}
As for the function remove_last then the function parameter ret is a local variable of the function that will not be alive after exiting the function. The function parameter must have the type char **. That is the corresponding argument must be passed by reference to the function.
And in any case the function is incorrect because it ignores a case when the list contains only one node. You need to pass the pointer to the head node also by reference.
The function can look the following way.
int remove_last( node **head, char **ret )
{
int result = *head == NULL ? -1 : 0;
if ( result == 0 )
{
while( ( *head )->next != NULL ) head = &( *head )->next;
*ret = strdup( ( *head )->name );
free( *head );
*head = NULL;
}
return result;
}

Reversing a linked list list

I was trying to reverse a linked list but I kept comming across an issue. I finally figured out was wrong and fixed it, however I do not understand why my last approach doesn't work. The code below is the one that successfully reverses a linked list.
void reverse_list(list** head)
{
list* currNode = *head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
*head = currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
However, the code below does not work. Both use double pointers, but in this one I dereferenced head twice to get to the actual object and assigned it with *currNode. When I run this code, It ends up going into an infinite loop and its missing the last item. For example, if the items were 1,2,3,4,5, then the reverse would be 5,4,3,2, and it keeps printing the same list. I don't understand why this approach isn't working since I'm accessing the actual object (by derefrening twice) and assigning it with a new object (*currNode).
void reverse_list(list** head)
{
list* currNode = *head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
**head = *currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
The code below has the same issue as the one above. I followed the same approach as with the above code only this one uses a single pointer.
void reverse_list(list* head)
{
list* currNode = head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
*head = *currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
Any help to understand this would greatly be appreciated. Thank you!
Different levels of indirection.
Given list** head, *head is the value pointed to by head, a pointer to a Node, a list *. We dereference one more time and **head dereferences to list * and then to list. We're all out of pointers and and are accessing the object at the end of the line. So
**head = *currNode;
dereferences both pointers all the way back to the objects. This is assigning values to objects, not addresses to pointers to objects. Rather than changing the linkage of the list by updating pointers, whatever list head ultimately pointed at has been changed to match currNode, breaking the integrity of the list.
*head = currNode;
only derefences head one level closer to the object. Here we are operating on pointers and changing linkage.
In the final example we have
*head = *currNode;
and this is similar to the first failed attempt. It is assigning values rather than changing the pointers.
For starters all the three functions in general can invoke undefined behavior when they are called for empty lists due to these lines
list* currNode = head;
//...
tmpNode = currNode->next;
because there is used a null pointer to access memory.
In the second function in this if statement
if(tmpNode == NULL)
{
**head = *currNode;
break;
}
the original first node is overwritten by the last node
**head = *currNode;
So after exiting the function you will have that the original pointer head still points to the originally first node that was overwritten by the last node of the list. So as a result one actual data will be lost and there will be a memory leak.
As for the third function then it is enough to point to that the function accepts the pointer to the head node by value.
void reverse_list(list* head)
That is the function deals with a copy of the value of the original variable head. Changing the copy will not influence on the original pointer. The original pointer will still point to the first node.
The function can look simpler the following way
void reverse( list **head )
{
list *current = *head;
*head = NULL;
while ( current != NULL )
{
struct Node *tmp = current;
current = current->next;
tmp->next = *head;
*head = tmp;
}
}
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
} list;
int push_front( list **head, int data )
{
list *new_node = malloc( sizeof( list ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void display( const list *head )
{
for ( ; head != NULL; head = head->next )
{
printf( "%d -> ", head->data );
}
puts( "null" );
}
void reverse( list **head )
{
list *current = *head;
*head = NULL;
while ( current != NULL )
{
list *tmp = current;
current = current->next;
tmp->next = *head;
*head = tmp;
}
}
int main(void)
{
const int N = 10;
list *head = NULL;
display( head );
reverse( &head );
display( head );
putchar( '\n' );
for ( int i = 0; i < N; i++ )
{
push_front( &head, i );
}
display( head );
reverse( &head );
display( head );
return 0;
}
The program output is
null
null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null

Pop method for linked list behaves unexpectedly

I have two different linked lists, and I try to pop one node from the first linked list and to to the second linked list. My goal when calling the pop function (pop(Node * head)) is to update the new head and return to the popped node. However, what happens is that it turns to the popped node but the passed Node * head points to the popped element. Hence I cannot proceed. What is my problem with this function? I'm probably messing up with the pointers
Node * pop(Node * head)
{
Node * temp = head;
long val = temp -> value; //store the value before deallocating
head = head -> next;
free(temp);
return createNode(val);
}
//Code snippet where I call the pop function
if (currNode == NULL) {
headSublistPointers -> node = pop(tmpHeadOrigArr);
} else {
while (currNode -> next != NULL) {
currNode = currNode -> next; //go until the end of the linked list
}
currNode -> next = pop(tmpHeadOrigArr);
}
This function
Node * pop(Node * head)
{
Node * temp = head;
long val = temp -> value; //store the value before deallocating
head = head -> next;
free(temp);
return createNode(val);
}
does not make sense.
For starters the head shall be passed by reference
Node * pop(Node **head);
and secondly instead of freeing the popped node you could just return it setiing its data member next to NULL. Also in general the head node can be equal to NULL.
The function can look like
Node * pop( Node **head )
{
Node *current = *head;
if ( *head != NULL )
{
*head = ( *head )->next;
current->next = NULL;
}
return current;
}

Why to return pointer after inserting node in Link Lists

I don't understand why we have to return pointers to the head node after a node has been added to a linked list.
struct node *append(int v) {
struct node *ptr;
struct node *t;
ptr=head;
while (ptr->next != tail) ptr = ptr->next;
t=(struct node *) malloc(sizeof *t);
t->value=v;
t->next = tail;
ptr->next = t;
return ptr; // why return ptr?
}
It depends of the context of your problem. Usually in linked list problems you have to return the head of the list so you can have your data structure consistent, but I infer from your code that the head is a global variable.
Unless the code that are invoking this function needs to know the location of the new node created, you don't need to return the node since your head is a global variable (again, if my assumption is correct).
Besides this, I suggest to revise your function since I see some cases you are missing here (what if the list comes empty and you have to change the head for example?)
First of all the function is invalid. When the list is empty and the first element is added head can be equal to NULL. So using expression head->next that in the function code corresponds to ptr->next results in memory access violation.
Also in my opinion it is not a good idea to name NULL as tail. I would explicitly use NULL. Otherwise the code can confuse readers.
Neverteless the function can look the following way
struct node * append( int v )
{
struct node *t = ( struct node * )malloc( sizeof( *t ) );
t->value = v;
t->next = tail;
if ( head == tail )
{
head = t;
}
else
{
struct node *current = head;
while ( current->next != tail ) current = current->next;
current->next = t;
}
return t;
}
As for your question then I also do not understand why the function returns ptr instead of t. It should return t. In this case the function would be more correct and efficient because it would return head when the first element is appended to the empty list and when a next non-first value is appended to the list and the head would be some node (not necessary the head itself) the while loop had no iteration because the first expression ptr->next would be equal already to NULL.
The function should return the last appended node as in the implementation I have shown.
I am sure that it is simply an error in the design of the function.:)
This drawback will be more visible if do not use the global variable head within the function but declare it as a function parameter. For example
struct node * append( struct node *head, int v )
{
struct node *t = ( struct node * )malloc( sizeof( *t ) );
t->value = v;
t->next = tail;
if ( head == tail )
{
head = t;
}
else
{
struct node *current = head;
while ( current->next != tail ) current = current->next;
current->next = t;
}
return t;
}
In this case the function call in main could look for example the following way
struct node *head = NULL;
struct node *current;
int i;
head = append( head, 0 );
for ( i = 1, current = head; i < 10; i++ ) current = append( current, i );
In this case the call of append would be very efficient because within the function the while loop would not make even one iteration.
Linked lists are often implemented with a NULL head when the list is empty. In this case, you have to return the head of the list when the head is a local variable, or when it is a global and you want to have more than one list using the same function. When adding a node, the head may have changed, even if adding at tail, in the case the list was empty there is a new head.
But another way to do is to always have a node at the head, just here to indicate the head, and adding/reading/deleting nodes after this head in order to treat datas in the list. In this case, returning the head is useless, because the head never change.
You can detect the tail of the list when for a node n: n->next == NULL, using a head equal to NULL when the list is empty, as the following code do:
// add at the tail of the list:
struct node * add_to_list_tail(struct node * head,int v) {
struct node *t;
t=(struct node *) malloc(sizeof(struct node));
t->value = v;
t->next = NULL; // next is set to NULL because it is the new tail
if(head == NULL) {
// in the case the list is empty
head = t; // t become the new head, because the list was empty
} else {
// in the case the list isn't empty
struct node * n = head;
// the following loop will stop when n->next == NULL, and n will point to the tail
while(n->next != NULL) {
n = n->next;
}
n->next = t;
}
return head;
}
// add at the head of the list:
struct node * add_to_list(struct node * head,int v) {
struct node *t;
t=(struct node *) malloc(sizeof(struct node));
t->value = v;
t->next = head; // make t become the new head
return t; // return t because it is the new head
}
int main() {
struct node * head = NULL;
head = add_to_list(head,1);
head = add_to_list(head,2);
head = add_to_list_tail(head,3);
}
If you don't want to return the head, you can also pass a pointer to the head pointer, as parameter for the functions that manipulate the list. A short sample code:
void add_to_list(struct node ** head,int v) {
struct node *t;
t=(struct node *) malloc(sizeof(struct node));
t->value = v;
// make t become the new head:
t->next = *head;
*head = t;
}
int main() {
struct node * head = NULL;
// we pass &head, the adress of variable head, as parameter:
add_to_list(&head,1);
add_to_list(&head,2);
struct node * n = head;
while(n != NULL) {
printf("\nvalue: %d",n->value);
n = n->next;
}
}

Resources