Need help checking a linked list in C - 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.

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.

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...

passing values in a linked list add function

I have spent the last 2.5 hours creating this linked list and trying to understand why it is not passing a memory address to the head of the list. I'm trying to understand linked lists in C before I move onto what my class is learning about data structures in java. I looked into other questions and I don't understand why it doesn't work. Please excuse the comments I've been making an effort to understand everything. Thanks in advance for your time and help!
The head variable is equal to NULL after the new assignment
head = addFamMember(NULL); in the main thread. But I can see that memory has been allocated inside the add function by printing it's members (name, age, and next pointer). Here's the output:
Enter command to add, print, or quit: add
Enter name and age: brett 28
Added:brett Age:28 POINTING to:(null)
Enter command to add, print, or quit:
Here's the code: I left he comments to possibly help describe my thinking and pinpoint where I'm going wrong.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
typedef struct S_Family{
char name[16];
int age;
struct S_Family *next;
}Family;
//prototypes
Family *addFamMember (Family *previous);
void CleanUp(Family *start);
void PrintList(Family *start);
int main(){
setvbuf(stdout, NULL, _IONBF, 0);
printf("Enter command to add, print, or quit: ");
char input[16]; //fgets var to store input
char command[16]; //sscanf var to store read info from input
Family *head = NULL; //For a linked list we need to set up the first node and point it NULL
Family *newest = NULL; //We also need a pointer dedicated to updating the latest created node
//This while loop will continue to get input until the command "quit" is typed
//It includes the functionality of printing the list and adding new nodes
while( fgets(input, 15, stdin)){
sscanf(input, "%s", command);
if ( strcmp(command, "quit") == 0) {
printf("\n\nBreaking.........");
break;
} else if ( strcmp(command, "print") == 0) {
PrintList(head);
} else if ( strcmp(command, "add") == 0) {
if ( head = NULL){
head = addFamMember(NULL); //If there are no nodes give head a memory address so now we do (recursion somewhat?)
printf("head:%s ", head->name); //this doesn't print!! Head = NULL above for some reason.
newest = head; //newest cannot stay equal to NULL. this allows us to pass it as a param to add function w/out the start being NULL anymore
printf("newest:%s ", newest->name);
} else {
newest = addFamMember(newest); //Recursion where the new node gets a mem address and gets the previous node as guide to cont. the list
} //now we go to the add function
}
printf("\nEnter command to add, print, or quit: ");
}
CleanUp(head);
return 0;
}
/*We want to return a new family member structure
so we start of with a Family return type. The param
as mentioned before needs to be a pointer to the address
of the previous node. That node will be pushed away from the
NULL in a singly or doubly linked list */
Family *addFamMember (Family *previous) {
/*Now we need to get the member variable info for that newFamMember
and store into the newly created data structure newFamMember*/
char input[16];
printf("Enter name and age: ");
fgets(input,15, stdin);
Family *newFamMember = malloc(sizeof(Family)); //create new address for newFamMember
sscanf(input, "%s %d", newFamMember->name, &newFamMember->age); //takes the input (first a string then a integer) and stores it into its proper place
printf("Added:%s Age:%d POINTING to:%S \n\n",newFamMember->name,newFamMember->age, newFamMember->next->name);
newFamMember->next = NULL; //initialize it's pointer member var but more importantly maintains the linked list by pointing to null.
/*Now we tell the computer what to do if this isn't the first node
or not. If it is then there isn't a previous node so there is no
way to set any other nodes' pointers to point to something else*/
if ( previous != NULL){
previous->next = newFamMember; //if previous is not equal to NULL set the previous' next pointer to newFamMember
printf("previous:%s ", previous->next->name);
}
return newFamMember; //we always want to return a newly added family member. That's this function's purpose
}
/*now we can print the list*/
void PrintList (Family *head) { //start is a pointer so we can pass the value of start
Family *currentMember = head; //we create currentMember and set it equal to start so we can iterate through the list and print each one
int count = 0;
if (currentMember == NULL){
printf("There are no family members\n");
}
while (currentMember != NULL) {
count++;
printf("\n\nmember:%d Name:%s Age:%2d POINTING TO:%s\n",
count, currentMember->name,
currentMember->age,
currentMember->next->name);
currentMember = currentMember->next; //move to the next node in the list headed towards NULL
}
}
void CleanUp(Family *head){
Family *freeMe = head;
Family *holdMe = NULL;
while(freeMe != NULL) {
holdMe = freeMe->next;
printf("\nFree Name:%s Age:%d\n",
freeMe->name,
freeMe->age);
free(freeMe);
freeMe = holdMe;
//PrintList(*start);
}
}

C Segmentation Fault - linked lists [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I just started programming in C a few weeks ago and I am getting a segmentation fault in my program.
I believe it is because of those lines:
for (int i =0; i < HOW_MANY; i++)
{
people = people -> next;
if (people -> next == NULL) return people;
} //for
}//else
Here is my C program with comments based on my psedocode
struct person *insert_end (struct person *people, char *name, int age) {
//create a new space for the new person
struct person *pointer = malloc(sizeof(struct person));
// check it succeeded
if(pointer == NULL)
{
printf("The program could not allocate memory ");
exit(-1);
}
// set the data for the new person
strcpy(pointer -> name, name);
pointer -> age = age;
pointer -> next = people;
// if the current list is empty
if (people == NULL)
{
// set the new person's "next" link to point to the current list"
pointer -> next = people;
// return a pointer to the new person
return pointer;
}
else
{
// we need a loop to find the last item in the list
// (the one which as a "next" link of NULL)
for (int i =0; i < HOW_MANY; i++)
{
// set the "next link of this item to point
// to the new person, so that the person
// becomes the last item in the list
// (the next person should have a "next" link of NULL)
people = people -> next;
if (people -> next == NULL)
return people;
} //for
}//else
// return the start of the list
return pointer;
}
Also, let me know in case you need my full C code for the program, since this is only a method
Thank you,
Sarah :)
This is totally a question for Code Review, but since you did ask a question regarding how to implement an algorithm, at least I think it is suitable for Stack Overflow.
For starters, this is just a syntax thing, but I think you should replace this:
(*pointer).age = age;
with this
pointer -> age = age;
This notation is a lot cleaner in my opinion, and was incorporated into C for the purpose of accessing elements from a struct pointer. Other than that the code you have so far is missing some closing braces. Here's a version of your code with proper syntax, and other important stuff (such as what #H2CO3 mentioned):
struct person *insert_end (struct person *people, char *name, int age) {
//create a new space for the new person
struct person *pointer = malloc(sizeof(struct person));
// check it succeeded
if(pointer == NULL)
{
printf("The program could not allocate memory ");
exit(-1);
}
// set the data for the new person
strcpy(pointer -> name, name);
pointer -> age = age;
pointer -> next = people;
// if the current list is empty
if (person == 0)
{
// set the new person's "next" link to point to the current list"
pointer -> next = people;
// return a pointer to the new person
return pointer;
}
else
{
// we need a loop to find the last item in the list
// (the one which as a "next" link of NULL)
for (int i =0; i < HOW_MANY; i++)
{
// set the "next link of this item to point
// to the new person, so that the person
// becomes the last item in the list
// (the next person should have a "next" link of NULL)
lastItem -> next = people;
}
// return the start of the list
return pointer;
}
}
Lastly, since I don't have a full copy of your entire code, nor do I want it, I can provide you advice on how to implement the portion of pseudo-code you are unsure of.
use a loop to find the last item in the list (i.e. the one which has a
"next" link of NULL)
Finding the last item in a linked list is simple. The following function I have crafted below shows how you can obtain a specific node in a linked list at a certain index:
NODE* getNode(NODE* start, int index)
{
int i;
for(i = 0; i < index; i++)
{
start = start -> next;
}
return start;
}
This code can be modified so it instead returns the last node.
NODE* getLastNode(NODE* start)
{
for(;;)
{
start = start -> next;
if(start -> next == NULL)
return start;
}
}
The code above traverses each node in the list until it hits one that has it's connected node as NULL. It then returns that last node.
Now you can call said function above and get the last node.
set the "next" link of this item to point to the new person so the new
person becomes the last item in the list (i.e. the new person should
have a "next" link of NULL)
I'm sure you know how to do this judging from the code you provided above.

Single linked list

I have created a single linked list. Everything works fine.
I just want to know if I have done anything potentially dangerous in my code. The code snippets I am concerned about is my push, pop, and clean-up. The parts of the code is just for user interaction so not really important (I posted anyway so that it was more clear in what I was doing). Just the linked list application.
Many thanks for any suggestions, as this is my fist attempt.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct product_data product_data_t;
struct product_data
{
int product_code;
char product_name[128];
int product_cost;
product_data_t *next;
};
static product_data_t *head = NULL;
static product_data_t *tail = NULL;
static product_data_t *new_product = NULL;
// Push a product on to the list.
void push(int code, char name[], int cost);
// Pop (delete) a product from the list.
void pop(int code);
// Display all product in the list.
void display_list();
// Delete all memory allocated on the list
void clean_up();
// Display menu
void menu();
int main(void)
{
menu();
getchar();
return 0;
}
void push(int code, char name[], int cost)
{
// Allocate memory for the new product
new_product = calloc(1, sizeof(product_data_t));
if(!new_product)
{
fprintf(stderr, "Cannot allocated memory");
exit(1);
}
/* Populate new products elements fields */
new_product->product_code = code;
strncpy(new_product->product_name, name, sizeof(new_product->product_name));
new_product->product_cost = cost;
new_product->next = NULL;
// Set the head and tail of the linked list
if(head == NULL)
{
// First and only product
head = new_product;
}
else
{
tail->next = new_product;
}
tail = new_product;
}
// Find the product by code and delete
void pop(int code)
{
product_data_t *product = head;
product_data_t *temp = NULL;
product_data_t *previous = head;
int found = 0; // 0 - Not Found, 1 - Found
if(!head)
{
puts("The list is empty");
return;
}
while(product)
{
if(product->product_code == code)
{
found = 1; // Found
// Check if this is in the first node - deleting from head
if(head->product_code == code)
{
temp = head;
head = head->next;
free(temp);
// Finished Deleting product
return;
}
// Check if this is the end node - deleting from the tail
if(tail->product_code == code)
{
temp = tail;
tail = previous;
free(temp);
// Finished deleting product
return;
}
// delete from list if not a head or tail
temp = product;
previous->next = product->next;
free(temp);
// Finished deleting product
return;
}
// Get the address of the previous pointer.
previous = product;
product = product->next;
}
if(!found)
{
printf("code [ %d ] was not found\n", code);
}
// Set all to null after finished with them
product = NULL;
temp = NULL;
previous = NULL;
}
// Traverse the linked list
void display_list()
{
// Start at the beginning
product_data_t *product = head;
while(product)
{
printf("===================================\n");
printf("Product code: \t\t%d\n", product->product_code);
printf("Product name: \t\t%s\n", product->product_name);
printf("product cost (USD): \t%d\n", product->product_cost);
printf("===================================\n\n");
// Point to the next product
product = product->next;
}
// Finished set to null
product = NULL;
}
// Release all resources
void clean_up()
{
product_data_t *temp = NULL;
while(head)
{
temp = head;
head = head->next;
free(temp);
}
head = NULL;
temp = NULL;
// End program - goodbye
exit(0);
}
void menu()
{
int choice = 0, code = 0, cost = 0;
char name[128] = {0};
do
{
fflush(stdin); // Flush the input buffer
puts("========= Welecome to linked list ===============");
puts("[1] Add new product to the list");
puts("[2] Delete a product from the list");
puts("[3] Display all products");
puts("[4] Exit and clean up");
printf("Enter your choice: ");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("Enter product code: ");
scanf("%d", &code);
printf("Enter cost: ");
scanf("%d", &cost);
printf("Enter name: ");
scanf("%s", name);
push(code, name, cost);
break;
case 2:
printf("Enter product code: ");
scanf("%d", &code);
pop(code);
break;
case 3:
display_list();
break;
case 4:
clean_up();
break;
default:
puts("Incorrect choice");
break;
}
}while(choice != 4);
}
From pop()
if(head->product_code == code)
{
temp = head;
head = head->next;
free(temp);
// Finished Deleting product
return;
}
In the case of there only being one item, 'head' and 'tail' would be pointing to the same node. However, if you pop this one item, 'head' will be adjusted but 'tail' will still be pointing to the free'd node. This will leave a bad pointer, which may cause your computer to explode.
Addendum: Similarly, 'new_product' will be dangling if you ever pop the last node that was pushed, and clean_up() will leave the 'tail' pointer dangling as well. Even if the code sample provided will never dereference these after they're free'd, dangling pointers in C code should always be treated as "potentially dangerous".
strncpy(new_product->product_name, name, sizeof(new_product->product_name));
if the string is longer than the size you have it won't be terminated correctly.
I see no reason why new_product should be global and every reason why it should not be.
It looks like you're on the right track, but there are issues. I would remove the global variables, and instead have a list_t struct (containing head and tail) that you pass into functions. As others have noted, you may also want to make the list generic by using (e.g.) a node_t type and void* data pointer.
Generally push and pop are used to refer to adding or removing an item at the beginning, not an arbitrary location (as you do); this is just a question of naming.
If you had product_name char *product_name instead, that would allow you to remove the length limitation as well as the need for strncpy. You would just have the caller allocate the string, and then free it in clean_up.
You could consider using a enum to improve your menu's readability. For "Check if this is in the first node - deleting from head" (same for tail), you should just compare head to product, not compare the codes.
After "tail = previous", you should set tail->next to NULL.
Agree with the issues raised by goldPseudo and thaggie/Steven.
In push(), replace strncpy() with strlcpy() to ensure the destination string is always NUL terminated.
In cleanup(), I'd suggest that you remove the exit(0); statement -- you don't need it. Exiting a programme from within a subroutine is generally not the best thing to do.
You should take away one lesson from creating your first singly linked list, and that is, singly linked lists are generally not very useful in the real world because:
They're too hard to manipulate. Just look at the complexity of your pop() subroutine.
Relatively slow because you have to start at the beginning of the list each time you want to retrieve an element from the list.
You should now attempt to write your first doubly linked list. While doubly linked lists are more complex to implement, they are easier to manipulate (especially when deleting an element) than singly linked lists.
Is there any reason you call exit(0) from clean_up function? I think this is potential dangerous, since you don't give a chance to the user to finish program correctly.
As well I would suggest you to use data encapsulation when you building up you data structure:
typedef struct
{
int product_code;
char product_name[128];
int product_cost;
list_node *next;
} list_node;
typedef struct
{
list_node* head;
list_node* tail;
list_node* current;
int size;
} list;
Also it's a good practice to use trail dummy node at the head of your list to make your code more generic.
Following normal naming convensions, push and pop are related to stacks - i.e. push() should add an item to the top of the stack (you add to the tail of the list, which is fine!), and pop() should return and remove the item from the top of the stack (you search for a named item anywhere in the list and remove it.)
Function names aside, I would suggest a more generic (abstract) implementation of the list, where the content of a node is a pointer to arbitrary data (which in your special case will later be a product_data). This way your linked list can be re-used for any content, and is easier to debug, read and to maintain.
It would also be a better idea not to have stuff global, but rather permit multiple instances of a list. The normal C way is to keep the data in a struct, and then to pass an instance as first argument to each function.

Resources