#include <stdio.h>
#include <stdlib.h>
typedef struct node{
struct node* next;
int value;
}Node;
int findLastNodeValue(Node* head){
while(head -> next != NULL){
head = head -> next;
}
return head -> value;
}
int main(){
Node *node1,node2;
node1 = (Node *)malloc(sizeof(Node);
node2 = NULL;
node1 -> next = node2;
findLastNodeValue(node1);
findLastNodeValue(node2);
return 0;
}
This code giving a segmentation fault. But I cannot find why does it occur. Can you help me with this one.
There are multiple issues with your code:
You are mallocing node1 yet you are not setting the value anywhere. This creates undefined behaviour whenever you try to access value - you might crash the program or you might get garbage data, which is usually worse since it then leads to other parts of your code acting weird.
You are not freeing the dynamically allocated memory. While in your case it's not such big of a deal it tells me that you are not familiar with the way dynamic allocation works (my belief is also strengthened by the first bullet point in this list). Whenever you malloc something, always free it (in C++ you have new and delete) and (to prevent unexpected behaviour) set the pointer to NULL.
node2 is not a pointer. The * in Node *node1, node2; applies only to the first variable. Each consecutive variable also needs a * otherwise it will be allocated on the stack.
By looking at your code it's clear that you want node2 to be a pointer (otherwise you wouldn't be assigning NULL as its value :)). In that case you are trying to access next of node2 but node2 is initialized to NULL:
int findLastNodeValue(Node* head){ // You are passing node2, which is NULL
while(head -> next != NULL){ // Can't access next of a NULL -> code breaks
head = head -> next;
}
return head -> value;
}
As a general rule do the following:
Try using a function that instantiates your node - since this is C you don't have a constructor I would suggest to write a function (or several depending on how much functionality you need) that generates a new node. This way you will ensure that at least there is no chance of not initializing a node
For example:
Node* createNode(int value) {
Node* node = (Node *)malloc(sizeof(Node));
if (!node) return NULL; // If malloc fails for some reason
node -> value = value;
node -> next = NULL;
return node;
}
Try using a function that deletes your node - if there is any chance of accessing a deleted reference again, set it to NULL and handle the NULL value accordingly
For example:
void deleteNode(Node** node) {
if (!*node) return;
free(*node);
*node = NULL;
}
Note that the code above doesn't delete what's reference by next since we only want to delete the node we are passing to the function. If you have previous (in the case of a double linked list), you will have to first access the next node, set its previous value to NULL and then delete your current node.
Whenever you pass a pointer, always check if that pointer is NULL before you do anything with the data that it's supposedly referencing. Combining this with the node creator function from the first point you can be certain that you are not passing some Node pointer that has not been initialized properly
Now as to your function in particular I would do the following:
int findLastNodeValue(Node* head) {
if (!head) return -1; // We have a null reference, so there is nothing else to do here; exit accordingly and check the return value to see if the function call has been successful
while(head -> next != NULL) {
head = head -> next;
}
return head -> value;
}
findLastNodeValue(node2) is your biggest problem. When you send NULL into findLastNodeValue, the very first thing you try to do is dereference the NULL pointer at the clause (head -> next != NULL).
To resolve this, you can check for and handle the case when head is NULL in your findLastNodeValue function before your while loop.
Related
I am trying to understand how the code below for creating a singly linked list works using a double pointer.
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void push(struct Node** headRef, int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = *headRef;
*headRef = newNode;
}
//Function to implement linked list from a given set of keys using local references
struct Node* constructList(int keys[], int n) {
struct Node *head = NULL;
struct Node **lastPtrRef = &head;
int i, j;
for(i = 0; i < n; i++) {
push(lastPtrRef, keys[i]);
lastPtrRef = &((*lastPtrRef)->next); //this line
if((*lastPtrRef) == NULL) {
printf("YES\n");
}
}
return head;
}
int main() {
int keys[] = {1, 2, 3, 4};
int n = sizeof(keys)/sizeof(keys[0]);
//points to the head node of the linked list
struct Node* head = NULL;
head = constructList(keys, n); //construct the linked list
struct Node *temp = head;
while(temp != NULL) { //print the linked list
printf(" %d -> ", temp->data);
temp = temp->next;
}
}
I understand the purpose of using the double pointer in the function push(), it allows you to change what the pointer headRef is pointing to inside the function. However in the function constructList(), I don't understand how the following line works:
lastPtrRef = &((*lastPtrRef)->next);
Initially lastPtrRef would be pointing to head which points to NULL. In the first call to push(), within the for loop in constructList(), the value that head points to is changed (it points to the new node containing the value 1). So after the first call to push(), lastPtrRef will be pointing to head which points to a node with the value of 1. However, afterwards the following line is executed:
lastPtrRef = &((*lastPtrRef)->next);
Whereby lastPtrRef is given the address of whatever is pointed to by the next member of the newly added node. In this case, head->next is NULL.
I am not really sure what the purpose of changing lastPtrRef after the call to push(). If you want to build a linked list, don't you want lastPtrRef to have the address of the pointer which points to the node containing 1, since you want to push the next node (which will containing 2) onto the head of the list (which is 1)?
In the second call to push() in the for loop in constructList, we're passing in lastPtrRef which points to head->next (NULL) and the value 2. In push() the new node is created, containing the value 2, and newNode->next points to head->next which is NULL. headRef in push gets changed so that it points to newNode (which contains 2).
Maybe I'm understanding the code wrong, but it seems that by changing what lastPtrRef points to, the node containing 1 is getting disregarded. I don't see how the linked list is created if we change the address lastPtrRef holds.
I would really appreciate any insights as to how this code works. Thank you.
This uses a technique called forward-chaining, and I believe you already understand that (using a pointer-to-pointer to forward-chain a linked list construction).
This implementation is made confusing by the simple fact that the push function seems like it would be designed to stuff items on the head of a list, but in this example, it's stuffing them on the tail. So how does it do it?
The part that is important to understand is this seemingly trivial little statement in push:
newNode->next = *headRef
That may not seem important, but I assure you it is. The function push, in this case, does grave injustice to what this function really does. In reality it is more of a generic insert. Some fact about that function
It accepts a pointer-to-pointer headRef as an argument, as well as some data to put in to the linked list being managed.
After allocating a new node and saving the data within, it sets the new node's next pointer to whatever value is currently stored in the dereferenced headRef pointer-to-pointer (so.. a pointer) That's what the line I mentioned above accomplishes.
It then stores the new node's address at the same place it just pulled the prior address from; i.e. *headRef
Interestingly, it has no return value (it is void) further making this somewhat confusing. Turns out it doesn't need one.
Upon returning to the caller, at first nothing may seem to have changed. lastPtrRef still points to some pointer (in fact the same pointer as before; it must, since it was passed by value to the function). But now that pointer points to the new node just allocated. Further, that new node's next pointer points to whatever was in *lastPtrRef before the function call (i.e. whatever value was in the pointer pointed to by lastPtrRef before the function call).
That's important. That is what that line of code enforces, That means if you invoke this with lastPtrRef addressing a pointer pointing to NULL (such as head on initial loop entry), that pointer will receive the new node, and the new node's next pointer will be NULL. If you then change the address in lastPtrRef to point to the next pointer of the last-inserted node (which points to NULL; we just covered that), and repeat the process, it will hang another node there, setting that node's next pointer to NULL, etc. With each iteration, lastPtrRef addresses the last-node's next pointer, which is always NULL.
That's how push is being used to construct a forward linked list. One final thought. What would you get for a linked list if you had this:
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node* next;
};
void push(struct Node** headRef, int data)
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = *headRef;
*headRef = newNode;
}
int main()
{
//points to the head node of the linked list
struct Node* head = NULL;
push(&head, 1);
push(&head->next, 2);
push(&head->next, 3);
for (struct Node const *p = head; p; p = p->next)
printf("%p ==> %d\n", p, p->data);
}
This seemingly innocent example amplifies why I said push is more of a generic insert than anything else. This just populates the initial head node.
push(&head, 1);
Then this appends to that node by using the address of the new node's next pointer as the first argument, similar to what your constructList is doing, but without the lastPtrRef variable (we don't need it here):
push(&head->next, 2);
But then this:
push(&head->next, 3);
Hmmm. Same pointer address as the prior call, so what will it do? Hint: remember what that newNode->next = *headRef line does (I droned on about it forever; I hope something stuck).
The output of the program above is this (obviously the actual address values will be different, dependent to your instance and implementation):
0x100705950 ==> 1
0x10073da90 ==> 3
0x100740b90 ==> 2
Hope that helps.
For my program, I need to create a function that accepts a linkedlist as a parameter, then deletes the first node from the list. There are other steps, but I'd like to get this sub-part done first.
This is what I have so far:
struct node *del_the_first(struct node *head) {
struct node *temp = head;
head = head->next;
temp->next = NULL;
return NULL;
}
I believe my solution is correct, however I have no way of testing it at this time. I'm more interested in why I am or am not wrong.
What you should test is:
print the value of temp at the end of the function,
this is what head was at the start of the function
print the value of head at the end of the function,
which is what the head of the list should be after returning for the function
print (from outside the function, e.g. from main) the value of the variable
which is supposed to point to the head of the list,
especially after deleting the first element
You will notice that outside your function the pointer to the head of the list is still pointing to where the first element still is.
You do not want that, do you? The variable which points to the head of the list is supposed to point to the second element of the list, isn't it?
If above is true, you probably want to use free() on the formerly first element of the list, before returning from the function.
Read this for more information on how to fix the first problem:
Parameter Passing in C - Pointers, Addresses, Aliases
Basically, you will want to return the new value of the pointer to the head of the list:
struct node *del_the_first(struct node *head)
{
struct node *temp = head;
head = head->next;
temp->next = NULL; /* not really needed */
free(temp);
return head;
}
Then call it like:
global_head = del_the_first(global_head);
Note that this code assumes that the list is not empty,
see the answer by ccpan on how to remove this assumption.
You need to check for boundary conditions. Suppose your linkedList is empty, then during runtime, you will get a segmentation fault. So you need to check if head pointer is NULL or not before trying to access next node.
Also, I don't know why you are returning a NULL. You are most probably wanting to return the new head node pointer.
struct node *del_the_first(struct node *head) {
if (head != NULL) {
struct node *temp = head;
head = head->next;
free(temp);
temp = NULL;
}
return head;
}
typedef struct n {
int data;
struct n *next;
}node;
This function deletes all nodes with odd values (without freeing memory or additional variables) :
void deleteOdds (node **head) {
if (*head == NULL) {
return;
}
while ((*head)->next) {
if ((*head)->data % 2 != 0) {
*head = (*head)->next;
} else head = &(*head)->next;
}
}
I understand the logic (and already have some assumptions), but I'm not sure how to explain the difference between
*head =(*head)->next;
and
head = &(*head)->next;
thanks in advance!
The key to understanding what's going on is to see what is being assigned in each case:
*head = (*head)->next; assigns to whatever head is pointing to, which is either the original head pointer, the pointer to which is passed to the function, or a next of some prior node. We modify what is pointed to, while the pointer stays the same. This amounts to deleting from the list (and creating a memory leak in the process).
head = &(*head)->next assigns to the head itself, i.e. it modifies the pointer, not the thing that it points to. This amounts to skipping ahead in the list, without modifying it.
Note: head is not the ideal name for the variable. Since the variable points to head pointer only until the first "skip", a better name for it would be current.
This is my current code which i have messed with so how im dealing with the first item or only item are wrong, which are the first 2 parts to this function. For some reason i am getting memory errors if i just try to set node=node->next_.. i would assume this would be the easiest way but when i throw that back into the program i start getting memory access issues. all other parts work fine as long as i don't manipulate the head address.
void removeNode(struct student_record_node* node)
{
struct student_record_node *temp=NULL;
temp=node;
if(node->next_==NULL&&node->prev_==NULL)
{
node=node->next_
free(node->prev_);
}
else if(node->prev_==NULL&& node->next_!=NULL)
{
node=node->next_
free(node->prev_);
}
else if(node->next_!=NULL && node->prev_!=NULL)
{
node->prev_->next_ = node->next_;
node->next_->prev_ = node->prev_;
student_record_node_deallocate(node);
}
else if(node->prev_!=NULL&& node->next_==NULL)
{
node->prev_->next_=node->next_;
student_record_node_deallocate(node);
}
}
There are several errors:
The node may be the head, so struct student_record_node* should be returned, instead of void.
node may be NULL
if node->prev_ is NULL, make sure to free the node deleted.
Since you did not provide, what your linked list looks like, I am going to assume for my code-example, that it is a struct containing a pointer to the head of the linked list (the first element). It is referred to by your_list.
The problem lies within the first two if-blocks in your code;
The first if: if(!node->next_ && !node->prev_):
This means you are deleting the head-element of the list. In this case, you will have to explicitly set the head to NULL, instead of setting the pointer to the node you wish to delete to NULL (by setting it to its predecessor, which is NULL). Also, you are freeing a NULL-Pointer, by freeing the previous node. This is in it self not a problem, but you want to delete node, not it's predecessor.
The second if: if(!node->prev_ && node->next_):
This means you are deleting the head, but the list will not be empty after the node is deleted. In this case, you must set the head of the list to point to the new head, which will be the node pointed to by node->next_. Also, you have the similar problem with free() as before.
Addressing those two points, your code should do something along the lines of this:
void removeNode(struct student_record_node *node){
if(!node->next_ && !node->prev_){
your_list->head = NULL; // Remove head - List is now empty.
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(!node->prev_ && node->next_){
your_list->head = node->next_; // Set the head to the new head.
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(node->next_ && node->prev_){
node->prev_->next_ = node->next_;
node->next_->prev_ = node->prev_;
student_record_node_deallocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
else if(node->prev_ && !node->next_){
node->prev_->next_ = NULL;
student_record_node_dealocate(node);
node = NULL; // Set freed pointer to NULL, for safety.
}
}
Here's my function to delete a linked list:
void deleteList( NODE* head )
{
NODE* temp1;
NODE* tempNext;
temp1 = head;
tempNext = NULL;
while( temp1 != NULL )
{
tempNext = temp1->next;
free(temp1);
temp1 = tempNext;
}
}
So temp1 first points where the head pointer is pointing. If it isn't NULL, tempNext will be set to point to the next element of the list. Then the first element (temp1) is free'd, and temp1 is reassigned to point to where tempNext is pointing and process repeats.
Is this the right approach to deleting an entire list?
I ask this because when I print the list after using this function, it still prints the list. And IIRC freeing something doesn't delete it but only marks it as available so I'm not sure how to tell if this is correct or not.
Your code looks correct.
You're also correct that freeing a list's elements doesn't immediately change the memory they pointed to. It just returns the memory to the heap manager which may reallocate it in future.
If you want to make sure that client code doesn't continue to use a freed list, you could change deleteList to also NULL their NODE pointer:
void deleteList( NODE** head )
{
NODE* temp1 = *head;
/* your code as before */
*head = NULL;
}
It still print the list, because you probably don't set the head pointer to NULL after calling this function.
I ask this because when I print the list after using this function, it still prints the list.
There is a difference between freeing a pointer and invalidating a pointer. If you free your whole linked list and the head, it means that you no longer "own" the memory at the locations that head and all the next pointers point to. Thus you can't garintee what values will be there, or that the memory is valid.
However, the odds are pretty good that if you don't touch anything after freeing your linked list, you'll still be able to traverse it and print the values.
struct node{
int i;
struct node * next;
};
...
struct node * head = NULL;
head = malloc(sizeof(struct node));
head->i = 5;
head->next = NULL;
free(head);
printf("%d\n", head->i); // The odds are pretty good you'll see "5" here
You should always free your pointer, then directly set it to NULL because in the above code, while the comment is true. It's also dangerous to make any assumptions about how head will react/contain after you've called free().
This is a pretty old question, but maybe it'll help someone performing a search on the topic.
This is what I recently wrote to completely delete a singly-linked list. I see a lot of people who have heartburn over recursive algorithms involving large lists, for fear of running out of stack space. So here is an iterative version.
Just pass in the "head" pointer and the function takes care of the rest...
struct Node {
int i;
struct Node *next;
};
void DeleteList(struct Node *Head) {
struct Node *p_ptr;
p_ptr = Head;
while (p_ptr->next != NULL) {
p_ptr = p_ptr->next;
Head->next = p_ptr->next;
free(p_ptr);
p_ptr = Head;
}
free(p_ptr);
}