Deleting a user-entered string node from a linked list - c

I am writing a function that is supposed to remove a node of the user's choice from a linked list of students. The program will take user input through a read_line function for the student's name, last name, and email and then 1)locate the node that has these elements, 2) bypass this node, and 3)release the memory occupied by that node. My function will delete any node other than the head node, and I cannot understand if I have an error in my logic.
Here is my function code:
struct student* remove_from_list(struct student *list){
struct student *p;
struct student *cur;
struct student *delete = malloc(sizeof(struct student));
if(delete == NULL){
printf("\nMalloc failed. \n");
}
if(list==NULL){
printf("\nRoster empty. Nothing to remove.\n");
return list;
}
printf("Enter the last name of the student to be removed: ");
read_line(delete->last, NAME_LEN);
printf("Enter the first name of the student to be removed: ");
read_line(delete->first, NAME_LEN);
printf("Enter the email of the student to be removed: ");
read_line(delete->email, EMAIL_LEN);
//to check if student is in the list
for(p=list; p!=NULL; p=p->next){
if(((strcmp(delete->last, p->last))!=0) || (strcmp(delete->first,p->first)!=0)
|| (strcmp(delete->email, p->email)!=0)){
continue;
}
else{
break;
}
printf("\nThis student does not exist.\n");
return list;
}
//to remove any element other than first
for(cur=list; cur->next!= NULL; cur=cur->next){
if(strcmp(cur->next->last, delete->last)==0 &&
strcmp(cur->next->email,delete->email)==0 &&
strcmp(cur->next->first, delete->first)==0){
delete=cur->next;
cur->next = cur->next->next;
free(delete);
printf("\nStudent has been removed from the list.\n");
return list;
}
}
cur=list; //to remove first element
if(cur->next == NULL){
cur=cur->next;
free(delete);
printf("\nStudent has been removed from the list.\n");
return list;
}
}
My function is not deleting the head node, and I'm not sure if it is a small fix or if there is something fundamentally wrong with my logic. I have seen examples that do this with integer input, but am struggling to implement this in my case. Any help or suggestions are appreciated.

Im not sure if this is what you were intending to do, but it looks wrong logically...
for(p=list; p!=NULL; p=p->next){
if(((strcmp(delete->last, p->last))!=0) || (strcmp(delete->first,p->first)!=0)
|| (strcmp(delete->email, p->email)!=0)){
printf("\nThis student does not exist.\n");
return list;
}
}
But isn't this going to fail unless the first node is the one you want to delete? You're beginning a loop, looking at the first node, and if it doesn't match, you return the list?
Instead of looking for the negative, I think the intention was to loop through the nodes, and if it finds a match, end the loop early - otherwise you want to get to the end of the list to check all of the nodes.
for(p=list; p!=NULL; p=p->next){
if(((strcmp(delete->last, p->last))==0) && (strcmp(delete->first,p->first)==0)
|| (strcmp(delete->email, p->email)==0)){
printf("\nFound a match.\n");
// delete this node and end this for loop early, probably better to use a while loop here, while(strcmp()!=0 etc);
}
}
EDIT
Now that you've got your for loop working to find the record, you might want to do another edit. You find the record with the set of strcmp()!=0 inside the first for loop, which if it finds the record, breaks out of the loop. At this point, you can delete the found record!
That means you don't need to repeat your loop again to delete the record, and you won't have two different blocks of code for if it is the first or subsequent record in the list.
//to check if student is in the list
for(p=list; p!=NULL; p=p->next){
if(((strcmp(delete->last, p->last))!=0) || (strcmp(delete->first,p->first)!=0)
|| (strcmp(delete->email, p->email)!=0)){
continue;
}
else {
// we found a match! Delete it
delete=p;
p->next = p->next->next;
free(delete);
printf("Student has been removed from the list.\n");
return list;
}
printf("\nThis student does not exist.\n");
return list;
}
// should never get here any more
} // end func.
If you really want to keep your code the way it is, because it does work for all but the first record, then you need to get the deleting of the first record to work. The check of == NULL looks wrong, because you only delete the first record if there are no others linked to it? Error in that logic, I feel.
// if we get here, we can assume that the first element matched,
// because otherwise we should never have gotten here
cur=list; //to remove first element
if(cur->next != NULL){ // if there is a next element
// set next to be the first
cur=cur->next;
}
free(delete);
printf("\nStudent has been removed from the list.\n");
return list;
}

Related

My first time working with linked lists. Would like some critiques and suggestions on how to fix certain issues

The program is for a hotel laundry service. The user puts in their room number, first and last name, and the number of items they want to wash. This information is put into a node in a linked list. In the main menu, the user can add more room requests, update a request, print the requests, or quit the program.
The structure can be defined as
struct request{
int room_number;
char first[NAME_LEN+1];
char last[NAME_LEN+1];
int num_items;
struct request *next;
};
I have run into a few issues with my functions:
The append function:
/*
APPEND FUNCTION:
Gets the room number, first name, last name, and the number of items the user wants to wash.
Creates a new node and appends it to the end of the linked list.
*/
struct request *append_to_list(struct request *list)
{
struct request *new_node, *last_node, *search;
new_node = malloc(sizeof(struct request));
//new_node->next = NULL;
if(new_node == NULL)
{
printf("Error allocating memory.\n");
return list;
}
//get room number
printf("Enter room number: ");
scanf("%d", &new_node->room_number);
//search to see if the room number already exists in the LL.
for(search = list; search != NULL; search = search->next)
{
if(search->room_number == new_node->room_number)
{
printf("Room request already exists. Update request using main menu.");
return list;
}
}
//get first and last name
printf("Enter first name: ");
read_line(new_node->first, NAME_LEN+1);
printf("Enter last name: ");
read_line(new_node->last, NAME_LEN+1);
//get the number of items.
printf("Enter the number of items you wish to wash: ");
scanf("%d", &new_node->num_items);
new_node->next = list;
//if list is empty, return pointer to newly created linked list.
if(list == NULL)
{
list = new_node;
return new_node;
}
//else add request to the end of the LL and return pointer to the LL.
else
{
last_node = list;
while(last_node->next!=NULL)
last_node = last_node->next;
}
last_node->next = new_node;
return list;
}
Some issues I have run into are that for some reason I cannot make more than two requests. I get an error and the program crashes.
The update function:
/*
UPDATE FUNCTION:
User enters their room number and the node containing the room number is updated with the number of items the user wants to add on.
*/
void update(struct request *list)
{
struct request *search;
int add_items;
//ask to enter room num
printf("Enter room number: ");
int room;
scanf("%d\n", &room);
//find matching room num
for(search = list; search != NULL; search = search->next)
{
if(search->room_number == room)
{
//ask to enter num of items to be added and update num of items
printf("How many items would you like to add: ");
scanf("%d\n", &add_items);
search->num_items = search->num_items + add_items;
search = search->next;
return;
}
}
//if room num is not found, print a message.
printf("Could not find request.");
return;
}
An issue I had with this function is that the program will just stop when I enter the room number... It does not crash, it just seems like it gets stuck... Not really sure why.
Finally the print function:
/*
PRINTLIST FUNCTION:
Prints all the nodes in list.
*/
void printList(struct request *list)
{
//print room num, first and last name, and num of items for all requests on the list.
while(list != NULL)
{
printf("%d ", list->room_number);
printf("%s ", list->first);
printf("%s ", list->last);
printf("%d\n ", list->num_items);
list = list->next;
}
}
My only issue with this function is that it infinitely prints all the nodes without stopping.
Any help is appreciated. Thank you!
You need to decide whether you want new_node at the end or the beginning of the list. list = new_node puts it at the beginning, the subsequent loop puts it at the end, so you create a cyclic list with no end and your next insert operation gets stuck in an infinite loop. If you want new_node to be at the beginning, you don't need to search for the end. If you want it at the end, then new_node->next must be set to NULL, not to list.
Your for loop body only gets executed once because you've got return in both arms of your if statement.
This is probably because of the bullet point 1 above.

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 first node in linked list still shows node in results

I'm having trouble with deleting the first node in my linked list, when i print the results after deleting other nodes its a success, yet deleting the first node, its prints a 0 and the last two members of the struct.
The function is supposed to be passed a pointer to a linked-list, prompt the user for an ID number to find a delete a node, and return the list.
struct dog *delete_from_list(struct dog *dogs){
int num;
printf("Enter a dogs ID number to be deleted ");
scanf("%d", &num);
struct dog *prev, *cur;
for(cur = dogs, prev = NULL;
cur !=NULL && cur->number != num;
prev = cur, cur = cur->next);
if (cur == NULL){
printf("Dog not found");
return dogs;
}
if( prev == NULL){
dogs = dogs->next;
printf("Dog deleted");
}
else{
prev->next = cur->next;
}
free(cur);
return dogs;
}
This is the function to print the linked list afterwards
void print(struct dog *list){
/* Prints all structs within the
* linked list
*/
printf("\nID Number\t Dog Name\t Breed\t\t Owner\n");
for( ; list != NULL; list = list->next){
printf("%d\t\t %-10s\t %-10s\t %-12s\n", list->number, list->dog_name, list->breed, list->owner_last_name);
}
}
Your function apparently works fine works fine (modified it to accept num as a parameter for me...), concerning its actual intention.
What you do not get is an output "dog deleted" if you do not delete the head - this is because you did not implement to do so. Try this instead:
if (!cur)
{
puts("Dog not found");
return dogs;
}
if(!prev)
{
dogs = dogs->next;
puts("head deleted"); // <- changed "dog" -> "head"
}
else
{
prev->next = cur->next;
puts("dog deleted"); // <- added by me!
}
Important is: You absolutely need to call it (as BLUEPIXIY denoted in his comment) like this:
dogs = delete_from_list(dogs);
If you do not, your outer variable 'dogs' won't change and will point to memory already deleted. If you still use the then dangling pointer, you get undefined behaviour, most likely an access violation (segmentation fault).
To avoid such a problem, you might like to pass a pointer to pointer to your function. The return value gets free then, and you could use it to indicate if the dog actually could have been removed:
bool // need to #include <stdbool.h> for
delete_from_list(struct dog** dogs)
// ^
You'd now use *dogs instead of dogs internally and would call it like this:
delete_from_list(&dogs);
Advantage: user does not have to care about correct re-assignment...

Need help checking a linked list in C

I'm having a issue on searching through a linked list. I'm making a grade book program and I'm doing input error checks to see if the user entered a existing course to enroll a student to said course.
So this is the struct for the course info with a doubly linked list.
typedef struct Course_Info // Course information
{
int Course_ID;
char Course_Name[15];
struct Course_Info *next;
} Course;
typedef struct // Linked list for Course
{
int Ctracker; // Keeps track of courses
Course *Course_Head;
Course *Course_Tail;
} List_Course;
And their corresponding variables along with initialization.
List_Student Students;
List_Course Courses;
Grade_List Grades;
Students.Stracker = 0;
Students.Student_Head = Students.Student_Tail = NULL;
Courses.Ctracker = 0;
Courses.Course_Head = Courses.Course_Tail = NULL;
Grades.Grade_cnt = 0;
Grades.Grade_Head = Grades.Grade_Tail = NULL;
In this function I'm going to enroll a student to a course but first im going to do some input checking to make sure the course exist.
void EnrollStudent(List_Course *Courses, List_Student *Students)
{
int CID; int SID;
printf("Enter course ID: ");
scanf("%d%*c", &CID);
if( CID != Courses -> Course_Head -> Course_ID)
{
printf("Course does not exist!\n");
return;
}
else
{
printf("Found class!\n");
}
}
The problem with what I currently have is that it only searches the first element of the linked list. How do I go about making a loop that checks the entire linked list?
Iterating a linked list is quite straightforward.
You need to use a local variable which is the current element of the list, which you init to Courses->Course_Head, eg:
Course* current = Courses->Course_Head;
then until current != NULL you just keep updating the current to point to the next element, eg:
while (current != NULL) {
// do what your want with current
current = current->next;
}
Mind that in your example you speak about a doubly linked list but it's a single linked list with two pointers to head and tail, a double linked list has two pointers for each node in both directions so that you can traverse it in reverse order, which is not the case in your situation.
ListCourse * current = Courses->Course_Head;
while ( (NULL != current) && (CID != current->Course_ID) ) current = current->next;
if (NULL == current) printf("Course %d not found\n", CID);
else printf("Course %d found\n", CID);
Your problem is that you are not iterating over the list, rather you are only checking the list head. You need to maintain a pointer to the node you are checking and iterate it (point it to the next node) in case you didn't find what you were looking for. You exit if there is nothing left to search or you found what you were looking for.

Why can't I delete the first node (head node) directly?

I have created a simple linked list using c where we can insert and delete elements at any place in the list. The code worked properly until i tried to delete the first node using the following code.
typedef struct l_list
{
int data,index;
struct l_list *next_node;
}node;
static int total_node=0;
node *search(node *,int,node **);
int main()
{
int choice,key;
char ans;
node *new_node,*head, *p_node,*index_change,*cur;
node *get_node();
head=NULL;
printf("Program for Linked List.\n");
do
{
printf("\n1. Create Node");
printf("\n2. Delete Node");
printf("\n3. Traverse the List");
printf("\n4. Exit");
printf("\nEnter your choice: ");
scanf("%d",&choice);
switch(choice)
{
case 1:
do
{
total_node++;
new_node=get_node();
printf("\nEnter the data you want to insert: ");
scanf("%d",&new_node->data);
if(head==NULL)
{
head=new_node;
head->index=1;
}
else
{
printf("\nWhich node you want to insert it as:\n");
for(int i=1;i<=total_node;i++)
{
printf("%d ",i);
}
printf("==) ");
scanf("%d",&key);
//printf("\b\b-|-");
if(key==1)
{
new_node->next_node=head;
head=new_node;
}
else
{
p_node=search(head,key,&cur);
new_node->next_node=p_node->next_node;
p_node->next_node=new_node;
//p_node=NULL;
}
new_node->index=key;
index_change=new_node->next_node;
while(index_change!=NULL)
{
index_change->index=++key;
index_change=index_change->next_node;
}
}
printf("\nDo you want to insert more node in the linked list: [y/n]");
//ans=getch();
}while((ans=getch())=='y');
break;
//Deletion code.
case 2:
do
{
if(head==NULL)//head is first node of the list
{
printf("\nUNDERFLOW!\nThe linked list is already empty.\n");
}
else
{
printf("Which node you want to delete:\n");
for(inti=1;i<=total_node;i++)
printf("%d ",i); //total_node=variable taken
printf("==) "); //to track the total no of node
scanf("%d",&key); //key=node index to be deleted
//printf("\b\b-|-");
if(key==1)
{
//If we need to delete the first node when only one node is left
if(total_node==1)
{
//p_node=head;
head=NULL;
}
//If we need to delete the first node when more than one node are there
else
{
//p_node=head;
head=head->next_node;
}
total_node--;
}
else
{
p_node=search(head,key,&cur);//returns node just before the node to be deleted
p_node->next_node=cur->next_node;//cur gets the value of the node that is to be deleted.
total_node--;
}
index_change=p_node->next_node;
while(index_change!=NULL)//to change the index of following nodes.
{
index_change->index=key++;
index_change=index_change->next_node;
}
}
printf("\nDo you want to delete more nodes: [y/n]\n");
}while((ans=getch())=='y');
case 3:
if(head==NULL)
printf("\nThe linked list is empty.\n");
else
{
printf("\nThe elements of linked lists are as follows:\n\n");
p_node=head;
while(p_node!=NULL)
{
printf("[%d]->%d ",p_node->index,p_node->data);
p_node=p_node->next_node;
}
}
break;
}
}while(choice!=4);
return 0;
}
node *get_node()
{
node *temp1;
temp1= new node;
temp1->next_node=NULL;
return temp1;
}
node *search(node *head,int key,node **cur)
{
node *current,*prev;
current=head;
while(current!=NULL)
{
if(current->index==key)
{
return prev;
}
prev=current;
current=current->next_node;
*cur=current;
}
return prev;
}
using this code if i try to delete the first node the program gets crashed. And when i use a temporary variable such as
if(key==1)
{
if(total_node==1)
{
p_node=head;
head=NULL;
}
else
{
p_node=head;
head=p_node->next_node;
}
total_node--;
}
the program works properly. So what i want to ask is can we delete the head node directly or we always require another temporary structure pointer to delete the head node.
In this line :
index_change=p_node->next_node;
you dereference p_node. But in the cases where you delete the first node, you don't set a value for p_node. The crash you observe is likely because p_node does not hold a valid memory address.
It is difficult to say what is happening without the exact error you are getting, and the complete program.
One thing that immediately looks wrong is that you assign nodes (your struct Linked_List), which includes the data. This is not what you want with a linked list. In a linked list, you just want to modify pointers, and prevent copying the data.
Also, you program is very badly structured. Consider moving the specific operations into separate functions, and write tests for each of these. This will also allow you to post a concise, complete code sample with your question.
In order to close in on the error, you can either use a debugger, or you can add print statements to your code that tell you what is happening.
In this line:
p_node=search(head,key,&cur);//returns node just before the node to be deleted
you pass head pointer which is already set to NULL if you try to delete 1st node.
So you can not dereference the head pointer inside the search function which your code must be doing (as I believe) since you do not have any other way to get to the start of linked list.

Resources