I'm just starting to learn linked lists and i was practicing by writing some basic functions such as add or delete. The program was working for some time but then i guess i did something and it starting giving me segmentation fault at my delete function which is this. The segmentation fault is in the while loop in if. Any idea why? Thanks in advance and sorry for my bad english :).
void deleteNode(struct node **first, int age)
{
struct node *tempNode;
if((*(*first)).age == age)
{
tempNode = *first;
*first = (*first)->next;
free(tempNode);
}
struct node *currentNode = (*first)->next;
struct node *lastNode = *first;
while(currentNode!=NULL)
{
//Segmentation Fault
if(currentNode->age == age)
{
tempNode = currentNode;
lastNode->next = currentNode->next;
free(tempNode);
}
lastNode = currentNode;
currentNode = currentNode->next;
}
}
The problem here is that you are referencing lastNode after it has been freed. If we look at the trace of the loop, you free tempNode, which you have assigned to currentNode, and then you set lastNode to currentNode. You want lastNode to point to the last valid node, so the update lastNode = currentNode is only valid if currentNode has not been freed.
In short, the fix is to only assign lastNode = currentNode if you don't remove the current node.
The trick with freeing nodes from a list is to get the information out of the node before you free it. Attempting to access memory that has been freed results in undefined behavior.
In this case you are only dealing with next, and you are tracking lastNode. So, lastNode->next is the logical currentNode.
In the beginning of your function, you account for the case that the first node matches age. However, if that is the case, the next node may also match age. So, your first matches age case also needs to be a loop.
Instead of writing two loops, use the concept of address of previous node's next to represent lastNode. This works well since first is already a pointer to a pointer to node.
struct node **lastNode = first;
while (*lastNode != NULL) {
struct node *currentNode = *lastNode;
if (currentNode->age != age) {
lastNode = ¤tNode->next;
continue;
}
/* remove currentNode */
*lastNode = currentNode->next;
free(currentNode);
}
Related
In our class right now we're covering nodes and linked lists, and are working on our first linked list program.
We've been given the following guidelines by the teacher:
Make sure your main function will accept 10 characters from STDIN and create a linked list with those characters (so your nodes will have a char member). Then, add an additional function called reverse. The purpose of the reverse function will be to create a copy of the linked list with the nodes reversed. Finally, print off the original linked list as well as the reversed linked list.
I've gotten it all written out, and I've compiled it with no errors - but the program doesn't work as intended, and I'm not entirely sure why. I'm sure it has something to do with how I've set up the pointers to "walk" the nodes - as the debug I put in shows it looping twice per user input letter. Specifications are that we're only supposed to use one function, and we pass a Node* to the function, and it returns the same. The function cannot print out anything - only make the second list that is a reverse of the first.
Any help would be greatly appreciated, I'm not terribly good at this yet and I'm sure I've made some rather silly mistakes.
#include <stdio.h>
#include <stdlib.h>
//struct declaration with self-reference to make a linked list
struct charNode {
char data;
struct charNode *nextPtr;
struct prevNode *prevPtr;
};
typedef struct charNode Node; //makes Node an alias for charNode
typedef Node *NodePtr; //makes NodePtr an alias for a pointer to Node (I think?)
//function declaration for a reverse function
Node* reverse(Node *stPtr);
int main(void)
{
//main function takes 10 letters and puts them in a linked list
//after that, it calls the reverse function to create a reversed list of those characters
//lastly it prints both lists
NodePtr newNode = NULL;
char input;
Node* revStart;
unsigned int counter = 0;
printf("Enter 10 letters to make a list: ");
NodePtr currentPtr = NULL; //sets currentPointer to startNode.
NodePtr previousPtr = NULL; //set previousPointer to null to start
while(counter<= 10)
{
scanf("%c", &input); //gather next letter
NodePtr newNode = malloc(sizeof(Node)); //creates a new node
if (newNode != NULL) //checks to make sure the node was allocated correctly
{
newNode->data = input; //makes the new node's data == input
newNode->nextPtr = NULL; //makes the nextPtr of the newNode NULL
}
currentPtr = newNode; //sets currentPtr to the address of the newNode
if(previousPtr == NULL) { //first time around previousPtr == NULL
newNode->nextPtr = newNode;
previousPtr = newNode; //sets previousPtr to the address of the new node (1st time only)
} else { //afterwards, currentPtr won't be NULL
previousPtr->nextPtr = currentPtr; //last node's pointer points to the current node
previousPtr = newNode; //update previous pointer to the current node
}
++counter;
//debug
printf("\nLoop #%d\n", counter);
}
revStart = reverse(newNode);
puts("The list is: ");
while (newNode != NULL){
printf("%c --> ", newNode->data);
currentPtr = currentPtr->nextPtr;
}
puts("NULL\n");
}
//reversing the nodes
Node* reverse(Node *stPtr)
{
//make a new node
NodePtr currentPtr = stPtr->nextPtr; //get the next letter ready (this will point to #2)
NodePtr prevRevPtr = NULL; //previous reverse node pointer
Node* revStart;
for(unsigned int counter = 1; counter <= 10; ++counter)
{
NodePtr revNode = malloc(sizeof(Node));
if(revNode != NULL) //if reverseNode is allocated...
{
if(prevRevPtr = NULL) //if previousReversePointer = NULL it's the "first" letter
{
revNode->data = stPtr->data; //letter = current letter
revNode->nextPtr = NULL; //this is the "last" letter, so NULL terminate
prevRevPtr = revNode; //previousReversePointer is this one
}else //after the first loop, the previous ReversePointer will be set
{
revNode->data = currentPtr->data; //set it's data to the pointer's data
revNode->nextPtr = prevRevPtr; //reverseNode's pointer points to last node entered
currentPtr = currentPtr->nextPtr; //moves to next letter
prevRevPtr = revNode; //changes previous reverse node to current node
if(counter == 10)//on the last loop...
{
revStart = revNode; //set revStart as a pointer to the last reverse node
//which is technically the "first"
}
}
}
}
return revStart;
}
Assuming your list is properly wired from inception, reversing a double-linked list is basically this:
Node *reverse(Node *stPtr)
{
Node *lst = stPtr, *cur = stPtr;
while (cur)
{
Node *tmp = cur->nextPtr;
cur->nextPtr = cur->prevPtr;
cur->prevPtr = tmp;
lst = cur;
cur = tmp;
}
return lst;
}
That's it . All this does is walk the list, swapping pointers, and retaining whatever the last node processed was. When done correctly the list will still be end-terminated (first node 'prev' is null, last node 'next' is null, and properly wired between.
I strongly advise walking a list enumeration through this function in a debugger. With each iteration watch what happens to cur as it marches down the list, to the active node's nextPtr and prevPtr values as their swapped, and to lst, which always retains the last-node processed. It's the new list head when done.
Okay so we don't need to contend with no line breaks in commments:
Node *reverse(Node *list) {
Node *rev = NULL;
while (list) {
Node *elt = list; // pop from the list
list = list->next;
elt->next = rev; // push onto reversed list.
rev = elt;
}
return rev;
}
As you wrote in comments, you probably don't need to have a previous pointer; you can just create a singly linked list.
There are several issues in your code, including:
When malloc returns NULL, you still continue with the loop -- only part of the code is protected by the if that follows, but not the rest. You should probably just exit the program when this happens.
Your algorithm does not maintain a reference to the very first node, i.e. the place where the linked list starts. When you print the list you should start with the first node of the list, but instead you start with newNode, which is the last node you created, so obviously not much will be printed except that last node. Moreover, both other pointers you have, will also point to the last node (currentPtr, previousPtr) when the loop ends.
newNode->nextPtr = newNode; temporarily creates an infinite cycle in your list. This is resolved in the next iteration of the loop, but it is unnecessary to ever make the list cyclic.
In the reverse function you have if(prevRevPtr = NULL)... You should get a warning about that, because that is an assignment, not a comparison.
Some other remarks:
The reverse function unnecessarily makes distinction between dealing with the first node and the other nodes.
It is also not nice that it expects the list to have 10 nodes. It would be better to just rely on the fact that the last node will have a NULL for its nextPtr.
It is a common habit to call the first node of a linked list, its head. So naming your variables like that is good practice.
As you are required to print both the initial list and the reversed list, it would be good to create a function that will print a list.
As you need to create new nodes both for the initial list and for the reversed list, it would be good to create a function that will create a node for you.
(I know that your teacher asked to create only one function, but this is just best practice. If this doesn't fit the assignment, then you'll have to go with the malloc-related code duplication, which is a pitty).
As you defined the type NodePtr, it is confusing to see a mix of Node* and NodePtr in your code.
Your question was first not clear on whether the reversal of the list should be in-place or should build a new list without tampering with the initial list. From comments it became clear you needed a new list.
I probably didn't cover all problems with the code. Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
struct charNode {
char data;
struct charNode *nextPtr;
};
typedef struct charNode Node;
typedef Node *NodePtr;
NodePtr reverse(Node *stPtr);
void printList(Node *headPtr);
NodePtr createNode(int data, NodePtr nextPtr);
int main(void) {
NodePtr headPtr = NULL; // You need a pointer to the very first node
NodePtr tailPtr = NULL; // Maybe a better name for currentPtr
printf("Enter 10 letters to make a list: ");
for (int counter = 0; counter < 10; counter++) {
char input;
scanf("%c", &input);
NodePtr newNode = createNode(input, NULL);
if (headPtr == NULL) {
headPtr = newNode;
} else {
tailPtr->nextPtr = newNode;
}
tailPtr = newNode;
}
NodePtr revHeadPtr = reverse(headPtr);
puts("The list is:\n");
printList(headPtr);
puts("The reversed list is:\n");
printList(revHeadPtr);
}
void printList(NodePtr headPtr) {
while (headPtr != NULL) {
printf("%c --> ", headPtr->data);
// You can just move the head pointer: it is a variable local to this function
headPtr = headPtr->nextPtr;
}
puts("NULL\n");
}
NodePtr createNode(int data, NodePtr nextPtr) {
NodePtr newNode = malloc(sizeof(Node));
if (newNode == NULL) { // If malloc fails, exit the program
puts("Cannot allocate memory\n");
exit(1);
}
newNode->data = data;
newNode->nextPtr = nextPtr;
return newNode;
}
NodePtr reverse(NodePtr headPtr) {
NodePtr revHeadPtr = NULL;
while (headPtr != NULL) {
revHeadPtr = createNode(headPtr->data, revHeadPtr);
// You can just move the head pointer: it is a variable local to this function
headPtr = headPtr->nextPtr;
}
return revHeadPtr;
}
typedef struct node node;
struct node {
int data;
node *next;
};
int insert_asc(node **phead, int data) {
node **traser;
node *newnode = malloc(sizeof(node));
if (newnode == 0)
return 0;
newnode->data = data;
for (traser = phead; *traser != 0; traser = &(*traser)->next)
if (data <= (*traser)->data)
break;
newnode->next = *traser;
*traser = newnode;
return 1;
}
The confusing part for me is when you dereference a double pointer traser.
how come (*traser)->next holds the next node's address?
and what exactly is *traser here?
Double pointers are used in the posted code for two separate purposes:
node **phead: the head of the list is passed by referenced so it can be updated by insert_asc if the new node must be inserted at the head of the list. Passing by reference is not possible in C, the idiomatic way to achieve it is to pass a pointer to the value to be updated by the function, hence a double pointer phead.
node **traser: To avoid making a special case of the empty list and the insertion at the head of the list, the programmer uses a pointer to keep track of the place to insert the new node. traser first points to the head of the list which in this case is the value of phead and is updated to point to the link between nodes, the next member of the current node, when it is determined that the new node must be inserted after the current one. This is an elegant way to implement insertion without a special case. C allows thanks to pointers, it is not possible in java nor javascript because these language do not have generalised pointers.
Note however that the code could be make more readable by use NULL instead of 0 when comparing pointers:
typedef struct node node;
struct node {
int data;
node *next;
};
int insert_asc(node **phead, int data) {
node **traser;
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
return 0;
newnode->data = data;
for (traser = phead; *traser != NULL; traser = &(*traser)->next) {
if (data <= (*traser)->data)
break;
}
newnode->next = *traser;
*traser = newnode;
return 1;
}
Note also that new nodes with a given value of data are inserted before nodes with the same data value. It does not make a difference in this case and may be a little faster for lists with many duplicates, but if the payload was more elaborate, this insertion method would implement a non-stable sort, whereas using < instead of <= would make the sort stable.
For illustration, here is alternative implementation that does not use a double pointer for the insertion point and needs extra tests for the special cases:
int insert_asc(node **phead, int data) {
node *cur;
node *newnode = malloc(sizeof(node));
if (newnode == NULL)
return 0;
newnode->data = data;
cur = *phead;
if (cur == NULL || cur->next == NULL) {
newnode->next = cur;
*phead = newnode;
} else {
while (cur->next != NULL && data < cur->next->data)
cur = cur->next;
newnode->next = cur->next;
cur->next = newnode;
}
return 1;
}
You are using a double pointer here in order to keep track of the head of your list.
If you were using a simple pointer here and exchanged the nodes, you would risk loosing the address of some nodes of your list.
This is because if you were passing a simple pointer to the head of your list, you would then manipulate a copy of you head address in your function, therefore when you exchange positions in your function, the address of your head would still be the same, if you exchanged the head with another node, then the address of all nodes before the old head would be lost after your function modifies your list.
Edit: pythontutor.com is a tool that helped me understanding the behavior of linked list quite easily thanks to its excellent visualization tool, I would highly recommend you to use it.
I have written two functions while learning Linked list. First counts and returns number of nodes in the list. Second adds new node towards the end of the list. Can you please help me understand why I need to use "current->next" to check for NULL in my code below in the addatend function? I did not have to use it for first function. Without that my exe crashes with bad pointer...
Thank you very much for your time and help.
int length(node * head) {
node * current = head;
int count = 0;
while(current != NULL){ // This line works as expected....
count ++;
current = current->next;
}
return count;
}
void addatend (node * head, int value){
node * newnode = (struct node *) malloc(sizeof(struct node));
node * current = head;
while (current != NULL){ // This line would not work?? If I use current->next != NULL it works.....
current = current->next;
}
current->next = newnode;
newnode->data = value;
newnode->next = NULL;
}
In the second function your are accessing current->MEMBER after you changed it to current->next, while in the first one you are not. That is, in the first one you just run to end, and end up with a NULL pointer, but you don't use it, so it's ok. In the second one, you do use it.
In order not to mess up with your original linked-list you copy the head to the current, and then you can do what you want with that current node.
Linked lists are generated like:
typedef struct e
{
int data;
struct e *next;
} node;
which means each node (in my example) has a data of type integer and a pointer to the next node. Let's suppose that our list has 4 objects:
1->2->3->4->NULL
the first node (data=1) point to its next node (data=2) and so on till last node (data=4) which the next node does not exist so will point to NULL.
in the function "addatend" in order to add new node to the end of the linked-list you should travel through the last element of the list (data=4 in my example), which the node next to it will point to NULL, so you should go through the list till the end and when you arrive there, you can add the "newnode".
I am rather new to C, and am working on copying a Linked List. It seg faults somewhere in the while loop, I think I am having some pointer troubles. Also, I'm not sure if I need to malloc each 'next' node. Do I? It makes sense for me to have to.
struct node* copyList() {
struct node* walker = head; // starting point to "walk" the list
struct node* temp;
temp = (struct node*)malloc(sizeof(struct node));
temp->data = walker->data;
while( walker != NULL ){ // done when we reach the node pointing to NULL
walker = walker->next; // advance walker to the next node in the list
temp = temp->next;
temp = (struct node*)malloc(sizeof(struct node));
temp->data = walker->data;
}
return walker;
}
The node strut is as follows
struct node {
int data;
struct node* next;
};
Suppose you reach last node..
Now inside loop , you increment walker..so now walker = NULL..
so this statement gives an error temp->data = walker->data..
Also you are just creating nodes and copying data not connecting your new linked list..
You need to maintain new Head pointer to return at the end
Keep reference to previous node so that you can link it to current node
Update the pointers
Change it along the lines of this..
struct node* copyList() {
struct node* walker = head; // starting point to "walk" the list
struct node* newHead=NULL,temp,prev=NULL;
while( walker != NULL ){ // done when we reach the node pointing to NULL
temp = (struct node*)malloc(sizeof(struct node)); //create new node
temp->data = walker->data; //copy data
if(prev==NULL) //if its first node
newHead = temp; //new head pointer
else
prev->next = temp; //else link to previous node
prev = temp; //update pointers
walker = walker->next;
}
return newHead;
}
Just where do you expect the value of temp->next in your loop to come from?
Also, to get a bit more meta, you might be vastly better off using e.g. std::list in C++ rather than implementing your own data structures like this. Even for experienced engineers, such efforts are notoriously error-prone.
I have a program in c which receives messages from different clients and servers. When the messages come in it adds the message to that list. After the message is added to the list I print it on the screen and on the other servers. But i want to delete the node that contains the message after it is printed so when the the print function gets called only prints the new messages. How can I delete the node after print?
Here is my struct:
typedef struct trade_list {
char* trader_msg;
u_int32_t id_of_sender;
int sender_timer;
int local_time;
struct trade_list *next;
}trade_list;
trade_list *head = NULL;
And here is how I print:
void print_trades()
{
trade_list * newnode = head;
trade_list *previous = NULL;
while (newnode) {
previous = newnode;
if ((elapsed - newnode->local_time >= 8))
printf ("%s\n", newnode->trader_msg);
newnode = newnode->next;
if (previous == NULL)
head = newnode->next;
else
{
previous->next = newnode->next;
free(newnode);
}
}
}
Thus gives me a segmentation fault. I tried changing the newnode->next to just newnode in the else part. previous->next = new node; It didn't give me an error but it did not erase the node as it kept printing that node every time the print function was called
At the start of your function:
trade_list *prev = NULL;
At each iteration in your loop, before newnode = newnode->next; add prev = newnode.
Then to delete:
if (prev == NULL) /* replace the head */
head = newnode->next;
else
prev->next = newnode->next;
/* free newnode? */
Fairly simple.
EDIT: You should really have some functions like list_create, list_add, list_remove associated with your data structure, rather than just chucking like the remove code into a print function. That's the first thing I do when I create any sort of data structure.
Another option is to have your linked list like:
typedef struct trade_node {
char* trader_msg;
u_int32_t id_of_sender;
int sender_timer;
int local_time;
struct trade_node *next;
} trade_node;
typedef struct trade_list {
trade_node *head;
/* trade_node *foot; ? */
size_t length;
} trade_list;
EDIT2: As for your edit, change print_trades to something like:
void print_trades()
{
trade_list *prev = NULL, *next;
trade_list *newnode = head;
while (newnode) {
if ((elapsed - newnode->local_time >= 8)) {
printf ("%s\n", newnode->trader_msg);
/* temp variable for newnode->next */
next = newnode->next;
if (prev == NULL) /* replace the head */
head = next;
else
prev->next = next;
/* free newnode->trader_msg? */
free(newnode);
/* don't update prev */
newnode = next;
}
else {
prev = newnode;
newnode = newnode->next;
}
}
}
In your code, previous will never be NULL since you set it to newnode at the start of the loop, and newnode also shouldn't equal newnode->next until after newnode has been processed completely. You're also using newnode after you've freed it.
Instead of having print_trades depend on the global variable head, make it receive this 'head' as an argument and print from there. It's much better design wise.
void print_trades(trade_list* head){ ... }
Then if you are adding the new nodes to the end of the list, you can print it starting from the first new one. If you are not using this information other than to print them, then there is no need to store them in a global list. Just use a local list within the function that receives them, and print that list.
Deleting a list is usually done by calling free(ptr) on each of the pointers. Since you know how to add nodes to the list, you will know how to inverse that appropiately.
Take some time to review Linked List handling and deletion of items from linked lists... once your "print" executes just do a delete...
Basically you want to have some trade_list Pointer objects that point to the node to-be deleted and the previous node. Have the previous node's "next" point to the node-to-be-deleted's next and then free the memory on the deleted node...
Start...
NODE1->NODE2->NODE3
^ ^
| |
*temp1 *temp2
temp1 = NODE1;
temp2 = NODE2; //or temp2 = temp1->next;
Next...
+-------------+
| V
NODE1 NODE2->NODE3
^ ^
| |
*temp1 *temp2
temp1->next = temp2->next;
free(temp2);
After...
NODE1-------->NODE3
//temp1 still = NODE1
//temp2 = null
Have the pointer objects iterate through the list with your while loop as you go so you don't end up out of sync. Also be sure to check for null