Having trouble deleting a node from a linked list - c

I've been working on this code for my shell that I'm creating and for some reason it isn't working. I'm implementing a watchuser function that watch's a user when an argument is given (args[1]). However, when a second argument (args[2]) of "off" is given, the user should be deleted from the linked list and should no longer be watched.
struct userList * goList;
goList = userInventory;
do{
if (strcmp(userInventory->username, args[1]) == 0){
printf("%s\n", args[1]);
printf("%s\n",userInventory->username);
struct userList * temp2;
temp2 = userInventory->next;
if (userInventory->next != NULL){
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
}
goList = goList->next;
}while (goList != userInventory);
My global struct is also as follows:
struct userList{
char * username;
struct userList * prev;
struct userList * next;
}
For reason, this code won't delete the user node from my linked list. The adding works, but this remove function won't and I'm not sure why. The print statements are there just to make sure it's executing the condition, which it is.
If anyone could help me find the reasoning behind my error, I'd greatly appreciate it. Till then, I'll be trying to debug this.
Thanks.

If I understand anything about your code,
Problem 1 (plausible):
goList = userInventory;
do {
...
goList = goList->next;
} while (goList != userInventory);
Is this a circular list? If it's not, the condition in while () isn't going to become true.
Problem 2:
goList = userInventory;
do {
if (strcmp(userInventory->username, args[1]) == 0) {
...
}
goList = goList->next;
} while (goList != userInventory);
Here you keep comparing the string in the head (or tail) of the list instead of comparing the string in the current node, goList. Finding a match can only succeed in the above code, if the match is in the very first node (head/tail) to which userInventory points initially.
Problem 3:
temp2 = userInventory->next;
if (userInventory->next != NULL) {
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
Let's assume userInventory is already corrected to be goList:
temp2 = goList->next;
if (goList->next != NULL) {
goList->next = temp2->next;
goList->next->prev = goList;
}
free(temp2);
First of all, it's going to free() not the matching node, but the one after it (or maybe even NULL), which is wrong.
Secondly, this piece of code isn't doing proper unlinking and relinking of nodes. What it should be (assuming the list isn't circular):
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
Problem 4:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
}
goList = goList->next;
} while (...);
If the deletion succeeds, this line is going to access the just freed node and likely crash your program:
goList = goList->next;
So, you need to change the code to something like:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
goList = goList->next;
free(temp2);
}
else
{
goList = goList->next;
}
} while (...);
Problem 5:
goList = userInventory;
If you delete the list head (or is it tail?) node, you need to update userInventory to point to the next node after it. If you don't, you will lose all access to the list because userInventory will point to freed memory and not to the remaining nodes, if any.
Problem 6 (plausible):
free(temp2);
The above line does not free() the memory behind temp2->username. You want to free() it if it was malloc()ed.
You should really approach problems one step at a time (e.g. first, iterating over a list, then unlinking/relinking nodes, then deleting nodes).
When things aren't clear or aren't working, use paper and a pencil (or a drawing board and a pen or a piece of chalk) to visualize the problem for yourself. Draw the objects, the arrows depicting pointers or some other connections between them, etc etc, scribble variable names next to the objects so you can clearly see how to progress from the diagram to code.

Related

Remove last node of a linked list, and add this to another linked list

I'm trying to figure out how to manipulate linked lists. I want to remove the end node of a given linked list and add this to the end of another given linked list.
For some reason I can not get my pointers right, well- at least that is where I think the problem lays.
(This method sits in a while loop, so one list keeps getting smaller while the other one grows.)
void movenode(struct Node **cards,struct Node **column)
{
struct Node *head = NULL;
struct Node *tmp,*head1 = *cards;
struct Node *tmp2,*head2 = *column;
if (*cards == NULL || (*cards)->next == NULL){
return;
}
while (tmp->next != NULL) {
head->next = tmp;
tmp = tmp->next;
}
while (tmp2->next != NULL) {
tmp2 = tmp2->next;
}
head->next = NULL;
tmp2->data = tmp;
tmp2->next = NULL;
*cards = head1;
*column = head2;
}
Hope someone is able to help me better understand this.
For some reason I can not get my pointers right, well.. atleast that is where I think the problem lays.
You're right, but it's a little more than that. For example, after struct Node *head = NULL; nothing modifies the value in head, so every time you do head->next you're accessing memory at "NULL + a small offset" (and will probably crash).
To remove the last entry from a singly linked list, you have to find the entry before the last entry (so that you can modify its next); and to add an entry to a linked list you have to find the last entry (so that you can modify its next). With this in mind we can break it into 5 parts:
Do sanity checks
Find the entry before the last entry in the *cards list
Remove the last entry in the *cards list
Find the last entry in the *column list
Add the (removed) entry to the end of the *columns list
You can implement each of these 5 pieces one at a time (and test them). This is an important part of programming - breaking more complex stuff into simpler easier things.
The resulting code might look something like (untested):
void movenode(struct Node **cards,struct Node **column) {
struct Node *temp = *cards;
struct Node *removed;
// Do sanity checks
if (temp == NULL) {
return;
}
// Find the entry before the last entry in the `*cards` list
if(temp->next != NULL) {
while(temp->next->next != NULL) {
temp = temp->next;
}
}
// Remove the last entry in the `*cards` list
if(temp == NULL) {
// The last entry was the first entry
removed = temp;
*cards = NULL;
} else {
removed = temp->next;
temp->next = NULL;
}
// Find the last entry in the `*column` list
temp = *column;
if(temp != NULL) {
while(temp->next != NULL) {
temp = temp->next;
}
}
// Add the (removed) entry to the end of the `*columns` list
if(temp == NULL) {
// There was no last entry (list was empty)
*column = removed;
} else {
temp->next = removed;
}
}
I'm not entirely sure about the mechanics of your solution so I'll offer a separate implementation and solution.
typedef struct Node {
void *data;
struct Node *next;
}
void poppush(Node *popHead, Node *pushHead) {
Node *pushLastNode = pushHead;
while (pushLastNode->next != NULL) {
pushLastNode = pushLastNode->next;
}
Node *popLastNode = popHead;
while (popLastNode->next != NULL) {
popLastNode = popLastNode->next;
}
Node *popSecondLastNode = popHead;
while (popSecondLastNode->next != popLastNode) {
popSecondLastNode = popSecondLastNode->next;
}
popSecondLastNode->next = NULL;
pushLastNode->next = popLastNode;
}
However, for operations such as these, I would recommend using a doubly-linked list and/or create some functions dedicated to managing the lists.

Adding a node to random spot in linked list

So I have a function that adds a node to a linked list but instead of just adding the element at the end of the linked list I am trying to get user input for the spot the user wants to add it in. I then need to add it and shift everything else over without deleting anything else. I am having a lot of trouble with this.
Below I will show my code, its a bit messy but I will explain as best I can.
void InsertGraphicElement(struct RasterGraphic *pA) {
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL)
return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL)
return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL) {
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
} else {
struct GraphicElement *tempHead = pA->GraphicElements;
struct GraphicElement *tempTail = pA->GraphicElements;
while (tempHead->pNext != NULL) {
tempHead = tempHead->pNext;
counter++;
}
tempHead->pNext = newNode;
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
return;
} else {
while (response < counter) {
tempTail = tempTail->pNext;
response++;
}
}
}
return;
}
It is incomplete Ive been messing with the code trying to figure it out but as you can see I am able to add without a problem at the end of the list. What I am having trouble with is if there are 5 elements for example in the list 1,2,3,4,,5 and I add a sixth the list would obviously look like this 1,2,3,4,5,6. What I want to do is take user input such as they want to add the sixth element to maybe spot 3 so the list would look like this 1,2,6,3,4,5. Ive tried a bunch of things a point in the right direction or some help would be greatly appreciated. Thank you,
Below are my struct definitions
struct GraphicElement {
char *fileName;
struct GraphicElement *pNext;
};
struct RasterGraphic {
//int numNodes;
struct GraphicElement *GraphicElements;
};
You currently have the following:
if (response == counter) {
return;
}
else {
while(response < counter){
tempTail = tempTail->pNext;
response++;
}
}
I would revise this to be something like:
if (response > counter+1 || response < 1) { // These are all invalid values
return;
}
else if (response == 1) { // We are replacing the head node
newNode->pNext = tempTail->pNext;
pA->GraphicElements = newNode;
}
else {
while(response-- > 2) { // Responses are 1-indexed
tempTail = tempTail->pNext;
}
newNode->pNext = tempTail->pNext; // IMPORTANT: Update newNode reference FIRST
tempTail->pNext = newNode; // Update tempTail to point to newNode
}
Disclaimer - Not tested in any way
I tried to comment things I thought were important here and there. The key thing to remember here though is that in order to insert something into a singly-linked list, you must update your new node to point to the rest of the linked list BEFORE you update the reference of the node previous to it, or else the last bit of your list will be lost forever and you will have a circular reference to the last node.
As a side note, it looks like your are traversing your entire linked-list twice each time you want to add a new element to it. I might recommend keeping a rolling counter of how many items are in the linked list that you update each time you add something to it to avoid the overhead of recalculating this value if efficiency is something you value.
Hope this helps!
EDIT: You'd have to remove the line that adds the new node to the end of the list higher up in the function for this to work, however. "tempHead->pNext = newNode;"

Deleting Node from Linked List based on User Input

Trying to understand how to properly setup a function that will delete from a linked list based on a user specified parameter. So I have a global variable entitled list that holds all the dog structs within them. I can get the list to delete the node the user wants removed if there are multiple structs in the linked list, for some reason though if there is only one node or the node I want deleted is the head of the list the function does not remove it. Any direction for this issue would be greatly appreciated.
void remove_one(char* name)
{
struct dog *tempList = list;
struct dog *previous = NULL;
if (tempList == NULL) {
return;
}
while (tempList != NULL) {
if (strcmpi(tempList->name, name) == 0) {
if (previous == NULL) {
tempList = tempList->next;
}
else {
previous->next = tempList->next;
free(tempList);
tempList = previous->next;
}
return;
}
else {
previous = tempList;
tempList = tempList->next;
}
}
return;
}
2 problems here:
if (previous == NULL) {
tempList = tempList->next;
}
First, tempList is a local variable, so you don't really delete the head. Next time you enter in the routine, list is still pointing to the same (valid) value.
Second, you don't free the memory.
correct code:
if (previous == NULL) {
list = tempList->next; // change global variable so head is "tempList->next"
free(tempList); // free templist memory else you get memory leak
}

Segmentation fault deleting nodes from singly linked list

this is the case i am working on
[11] -> [12] -> [13] -> NULL
I am trying to delete the elements from the liked list above(example) but I keep getting segfault and on running GDB doesnot help much. I am not looking for an answer but and explanation on where I am going wrong logically.
here is the code
int
List:: remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
if(headNode->_value == val){
tempNode = headNode->_next;
delete headNode;
_head = tempNode;
}
else
{
while(headNode->_value != val){
tempNode = headNode;
headNode = headNode->_next;
}
tempNode->_next = headNode->_next;
delete headNode;
}
}
You're not accounting for the following conditions:
The list may be empty; i.e. _head is NULL;
The value may not be in the list at all.
Your function is declared to return int, but makes no such return
Assuming the rest of your code is correct (and that is a big assumption), I'm all-but-certain this is what you're trying to do:
void List::remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
while (headNode && headNode->_value != val)
{
tempNode = headNode;
headNode = headNode->next;
}
if (headNode)
{
if (tempNode)
tempNode->next = headNode->next;
else
_head = headNode->next;
delete headNode;
}
}
Alternatively, if so inclined this can get (arguably) simpler utilizing a pointer-to-pointer to traverse the pointers in the list, not just their values. It is worth investigating how the following works, which still covers all the bases described previously, but does so using the actual pointers in the list nodes themselves, including _head, by-address rather than by-value, thereby eliminating the need for a walk-behind temporary pointer:
void List::remove( int val )
{
ListNode **pp = &_head;
while (*pp && (*pp)->_value != val)
pp = &(*pp)->next;
if (*pp)
{
ListNode *p = *pp;
*pp = p->next;
delete p;
}
}
In your remove method you are assuming there are always elements in your list. - What if it is empty?
What if the value isn't in the list? You need to handle this case as well.
You're headed in the right direction - there are just a few cases that you haven't considered that can lead you to seg fault.
Example of forward traversal with deletion (forward-only linked list):
// Start from the beginning (head), then while the current isn't null,
// move to the next node.
for (ListNode* current = head; current != null; current = current->next) {
// Check the next item if there is one, and remove it if it matches the value.
// We check the next one because you can't delete the current node in a
// forward only linked list (can in a doubly-linked list however)
if (current->_next != nullptr && current->_value == value) {
// Make this item point to the next next item
// (Since we're gonna delete the next item)
current->_next = current->_next->next;
// Delete the next item.
delete current->_next;
}
}

How to free only a single node in a linked list in C?

How can I free only a single node in a linked list? The following frees the whole linked list but I wanted to free only one node in the linked list.
//Here's my code for delete
while(headPtr!=NULL)
{
temp = headPtr;
headPtr = headPtr->next;
if(strcmp(temp->fname, stdfname) ==0 &&
strcmp(temp->sname, stdsname) ==0 )
{
free(temp);
}
}
You first need to know the previous node. Because of that, you need to iterate until you hit the node you want to delete. In that process you need to remember the previous node. Then you need to connection the previous and next nodes, thus "delinking" the node you want to delete.
currentNode = headNode;
previousNode = NULL;
while (currentNode != NULL) {
if (currentNode != nodeToDelete) {
// Not the node we want to delete yet,
// go on to next node.
previousNode = currentNode;
currentNode = currentNode->next;
continue;
}
// We've now hit the node to delete and know the
// previous node. Fix the structure.
if (previousNode) {
previousNode->next = nodeToDelete->next;
} else {
// No previous node means it's the head node.
headNode = nodeToDelete->next;
}
// The node is now delinked from list. Delete it.
free(nodeToDelete);
// Stop the loop.
break;
}
This is pretty bad performance-wise, which is why there are double-linked lists. There, the whole operations looks like this:
if (nodeToDelete->previous) {
nodeToDelete->previous->next = nodeToDelete->next;
}
if (nodeToDelete->next) {
nodeToDelete->next->previous = nodeToDelete->previous;
}
if (nodeToDelete == headNode) {
headNode = nodeToDelete->next;
}
free(nodeToDelete);
As you can see, no iteration is necessary here as each node knows its previous and next nodes.
BTW, to work these things out (they are pretty basic) it helps to draw a short linked list on a piece of paper. Draw boxes, in each box write the member names (like previous and next) and draw lines from these members to the corresponding other boxes. Then think about what is necessary to do in order to delete the node. It really helps you understand how this works.
The head of the list should never change unless the node you're deleting is the head. You should be moving the temp pointer down the list. You also need to fix up the links when you delete a node, note that there are three cases you need to be aware of and the case where it's the first node requires special handling. I'll give you the skeleton for the code, but since I don't know whether your list is singly- or doubly-linked, I'll leave the pointer updates to you.
temp = headPtr;
prev = NULL;
while (temp != NULL)
{
if (strcmp(temp->fname, stdfname) == 0)
&& strcmp(temp->sname, stdsname) == 0)
{
if (prev == NULL) { // head node
...
}
else if (temp->next == NULL) { // tail node
...
}
else { // interior node
...
}
break; // stop when done
}
prev = temp;
temp = temp->next;
}

Resources