Why is there an infinite loop here? (linked list printing) - c

I am doing a little practice with linked lists, these are the structures.
typedef struct roomList roomList;
typedef struct school school;
typedef struct studentList studentList;
roomList *getRoom(school* school, int class, int roomNr);
struct studentList{
char *name;
int class;
float grade;
int roomNr;
studentList *next;
studentList *prev;
};
struct roomList{
int nrOfStudents;
int roomNr;
studentList *students; //pointer to student list.
roomList *next;
roomList *prev;
};
struct school{
int totalStudents;
roomList *Class[13]; //array of classes, each index contains rooms.
};
This is where the infinite loop is happening, it's a function to print all students within a room.
void printRoom(school *school, int class, int roomNr)
{
roomList *room = getRoom(school, class, roomNr);
studentList *student;
if(room != NULL)
{
int i = 1;
printf("Nr of students: %d\n", room->nrOfStudents);
while(room->nrOfStudents != 0 && student != NULL)
{
student = room->students;
printf("%d - \"%s\" ",i, student->name);
student = student->next;
i++;
}
}
}
This is how I'm creating a student
studentList *createStudent(int class, char *name, int roomNr)
{
studentList *newNode;
newNode = (studentList*)calloc(1, sizeof(studentList));
newNode->class = class;
newNode->name = (char*)malloc(strlen(name)+1);
strcpy(newNode->name, name);
newNode->roomNr = roomNr;
newNode->grade = 0;
newNode->next = newNode->prev = NULL;
return newNode;
}
And finally, this is how I'm inserting a student into a room.
void insertStudentToRoom(school* school, int class, int roomNr, char *name)
{
roomList *room;
room = getRoom(school, class, roomNr);
studentList *newStudent;
newStudent = createStudent(class, name, roomNr);
if(room->students != NULL)
{
newStudent->next = room->students;
room->students->prev = newStudent;
room->students = newStudent;
room->nrOfStudents++;
school->totalStudents++;
}
else
{
room->students = newStudent;
room->nrOfStudents++;
school->totalStudents++;
}
}
The infinite infinite loop only happens when I insert more than one student into a room, and exits fine when there's only one student, I've tried fumbling around with exit conditions for my while() to no avail.

while(room->nrOfStudents != 0 && student != NULL)
{
student = room->students;
printf("%d - \"%s\" ",i, student->name);
student = student->next;
i++;
}
Look closely. You never change room in the loop. So student = room->students; is going to set the very same value for student every single time in the loop. If it didn't break after the first time, it won't break any other time.
You probably want to take student = room->students; out of the loop. You only want to point at the first student in the room once.

Related

Using loop to create new struct in C

Here, I have created a structure of student having name, roll no, marks, etc as their members. So, what I wanted to do was create a new struct every time it iterates through a loop and saving data in it as it loops through.
So, I created a variable stdname that changes it's value every iteration and use it as a way to name a new student but it's not working. I am fairly new to C and I don't know what is wrong with my code here.
#include <stdio.h>
struct Marks
{
int phy;
int chem;
int math;
};
struct Student{
char name[50];
int rollN0;
char remarks[100];
struct Marks marks;
};
int main()
{
for (int i=0; i<10; i++)
{
char stdname[50];
sprintf(stdname,"student%d",i+1);
struct Student stdname;
printf("Enter the following data of Student No. %d:\n", i+1);
//taking data from user and storing
}
}
I propose you to solve your problem by implementing a systeme of linked list or tabs. I personnaly perfere linked list because when your code will evolute it's going to be easier to used the list in my pov.
So in the code you will see in your structure i add a next pointer this one will store the adresse of another student, if next is NULL then we can affirm there is no more student and we can stop crawling throught the list.
the function add use this principe; she goes to the tail of the list then replace the next with the adresse of the new student structure. And because the next of the new structure is NULL this process it re-usable
The function create_new_node is only data initation and memory allocation.
It looks very ugly because i don't realy know what you want store.
❗ I didn't test this code and it's not optimised at all ❗
#include <stdio.h>
#include <stdlib.h> // for malloc function
struct Marks
{
int phy;
int chem;
int math;
};
struct Student{
char name[50];
int rollN0;
char remarks[100];
struct Marks marks;
struct Student* next; // I had this
};
/// new ///
void add_node_to_list(struct Student* node, struct Student* list)
{
while (list->next != NULL)
list = list->next;
list->next = node;
}
struct Student* create_new_node(struct Student data)
{
struct Student* new_node = malloc(sizeof(struct Marks));
if (!new_node)
return NULL;
new_node->name = data.name;
new_node->rollN0 = data.rollN0;
new_node->remarks = data.remarks;
new_node->marks = data.marks;
new_node->next = NULL;
return new_node;
}
/// end-new ///
int main(void /* new */)
{
struct Student* list = NULL;
struct Student* new_node = NULL;
for (int i=0; i<10; i++)
{
char stdname[50];
sprintf(stdname,"student%d",i+1);
/// new ///
if (!list) { // first the first element
list = create_new_node((struct Marks) {stdname, 0, "\0", {0, 0, 0}, NULL});
if (!list) // check for malloc errors
return 1;
} else { // every time there is a new student
new_node = create_new_node((struct Marks) {stdname, 0, "\0", {0, 0, 0}, NULL});
if (!new_node) // check for malloc errors
return 1;
add_node_to_list(new_node, list);
}
/// end-new ///
printf("Enter the following data of Student No. %d:\n", i+1);
//taking data from user and storing
}
return 0; // new: your main should always return a value. Compile with W flags see: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
}
Here is my propostion,
Notice me if i had been clumsy, i'm a new user on stack.
Also feel free to ask question or fix my own error
Hope I helped you 😊

wrong output when code runs problem with linked list?

Upon running the code below i get the output
NAME: (null) | GPA: 0.000000 | YEAR: (NULL)
are the linked lists not implemented correctly? I am currently using a makefile and bringing in a test.data file with names and gpa and senior/ect..
Ollie 2.9 freshmen
John 3.2 senior
Julie 2.2 freshmen
Joe 1.8 freshmen
Mary 3.8 senior
Sue 3.4 junior
Jane 2.7 senior
Bob 2.8 senior
Fred 3.2 freshmen
Bill 3.3 junior
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "students.h"
Student *top = NULL;
Student *temp, *temp1, *temp2;
// Creates the entire linked list from the file.
// Should call readNext and push
// Returns head of the linked list
Student *buildStudentList()
{
Student *p;
p = readNext();
push(&top, p);
return top; //TODO: Change return
}
//Read a single line from standard input and return a student structure located on the heap
Student *readNext()
{
Student *s =(Student*) malloc(sizeof(Student));
scanf("%s", s -> name);
scanf("%f", &s -> gpa);
scanf("%s", s -> year);
s->next = NULL;
return s; //TODO: Change return
}
//Return a student structure stored on the heap
Student *makeStudent(char *name, float gpa, char *year)
{
Student *s =(Student*) malloc(sizeof(Student));
s -> name = name;
s -> gpa = gpa;
s -> year = year;
s -> next = NULL;
return s; //TODO: Change return
}
//insert a new student node at the head of the linked list
void push(Student **list, Student *student)
{
top = *list;
student -> next = top;
top = student;
}
//Insert a student node in the desired position on the linked list
void insert(Student *list, Student *s, int position)
{
int i;
top = list;
temp = top;
for(i = 1; i < position -1; i++)
{
temp = temp -> next;
}
if(temp == NULL)
{
//blank
}
else
{
s -> next = temp -> next;
temp -> next = s;
}
}
//Displays contents of a single student structure
void display(Student *s){
printf("NAME:%s | GPA: %f | YEAR:%s
", s -> name, s-> gpa, s -> year);
}
//Displays contents of the entire linked list
void displayAll(Student *list)
{
temp = list;
while(temp != NULL)
{
display(temp);
temp = temp -> next;
}
}
//Delete all data allocated on the heap before terminating program
void cleanUp(Student *list)
{
temp1 = list;
temp2 = temp1 -> next;
while(temp1 != NULL)
{
free(temp1);
temp1 = temp2;
}
if(temp2 != NULL)
{
temp2 = temp2 -> next;
}
}
//Main function tests your functions.
int main()
{
printf("Program Started
");
//Construct Linked List from Standard Input
Student *list = buildStudentList();
//Insert a new student in desired position
Student *s = makeStudent("Max",3.0, "senior");
insert(list, s, 3);
//Display entire linked list
displayAll(list);
//Free all heap memory
cleanUp(list);
printf("Program Successful Exit
");
exit(EXIT_SUCCESS);
}
Since you didn't post your struct definition, I had to guess whether (e.g.) name was char *name; or (e.g. char name[100];). Within the code, it used it as a pointer.
So ...
Your readNext and makeStudent don't allocate space for the strings (char * pointers) name and year, so they're probably segfaulting.
insert takes Student *list when it really needs Student **list.
IMO, you should have a separate List type to avoid confusion (that has a single element: Student *head;). So, wherever you have Student *list, you replace it with List *list
When you do that, you don't have to pass down Student ** [a double star] pointer when you mean a list. Using list->head is a lot easier and more descriptive than *list.
Also, be consistent. Some functions take Student **list if they modify the list [they have to]. Others use Student *list, but they should be consistent as well.
No need for the various global scope temp variables. These should be function scoped and use more descriptive names.
Your insert has issues. It will orphan the node it's trying to insert if no position match is found (e.g. insert at position 99 in your example). Usual is to insert at tail or return an error code. Also, it wasn't totally clear what position meant [to me], because of the code you had. It could have been "insert before" or "insert after" the Nth node.
You can't insert a literal newline in a double quoted string. So, use the \n escape sequence (e.g.) printf("hello world\n");
Also, functions that take no arguments should use void (e.g.) instead of int main(), use int main(void).
Here's a cleaned up version of your code, incorporating what I've mentioned above:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//#include "students.h"
typedef struct student Student;
struct student {
Student *next;
float gpa;
char *name;
char *year;
};
typedef struct list {
Student *head;
} List;
//insert a new student node at the head of the linked list
void
push(List *list, Student *student)
{
student->next = list->head;
list->head = student;
}
//Return a student structure stored on the heap
Student *
makeStudent(char *name, float gpa, char *year)
{
Student *s = (Student *) malloc(sizeof(Student));
s->name = strdup(name);
s->gpa = gpa;
s->year = strdup(year);
s->next = NULL;
return s;
}
//Read a single line from standard input and return a student structure located on the heap
Student *
readNext(void)
{
char name[1000];
float gpa;
char year[1000];
Student *s = NULL;
int count = scanf("%s %f %s",name,&gpa,year);
if (count == 3) {
printf("readNext: name='%s' gpa=%g year='%s'\n",name,gpa,year);
s = makeStudent(name,gpa,year);
}
return s;
}
// Creates the entire linked list from the file.
// Should call readNext and push
// Returns head of the linked list
List *
buildStudentList(List *list)
{
Student *p;
while (1) {
p = readNext();
if (p == NULL)
break;
push(list, p);
}
return list;
}
//Insert a student node in the desired position on the linked list
int
insert(List *list, Student *s, int position)
{
Student *cur;
Student *prev;
int i;
int goflg;
//position -= 1;
#if 0
i = 1; // insert before Nth position
#else
i = 0; // insert after Nth position
#endif
prev = NULL;
for (cur = list->head; (cur != NULL) && (i < position);
++i, cur = cur->next) {
prev = cur;
}
// this may not be needed -- usual is to insert at tail if position is not
// found -- this will orphan the node to be inserted
#if 0
goflg = (i == position);
#else
goflg = 1;
#endif
if (goflg) {
s->next = cur;
if (prev != NULL)
prev->next = s;
else
list->head = s;
}
return goflg;
}
//Displays contents of a single student structure
void
display(Student *s)
{
printf("NAME:%s | GPA: %f | YEAR:%s\n", s->name, s->gpa, s->year);
}
//Displays contents of the entire linked list
void
displayAll(List *list)
{
Student *temp = list->head;
while (temp != NULL) {
display(temp);
temp = temp->next;
}
}
//Delete all data allocated on the heap before terminating program
void
cleanUp(List *list)
{
Student *cur;
Student *next;
for (cur = list->head; cur != NULL; cur = next) {
next = cur->next;
free(cur->name);
free(cur->year);
free(cur);
}
list->head = NULL;
}
//Main function tests your functions.
int
main(void)
{
List top = { NULL };
List *list;
printf("Program Started\n");
//Construct Linked List from Standard Input
list = buildStudentList(&top);
//Insert a new student in desired position
Student *s = makeStudent("Max", 3.0, "senior");
insert(list, s, 3);
//Display entire linked list
displayAll(list);
//Free all heap memory
cleanUp(list);
printf("Program Successful Exit\n");
exit(EXIT_SUCCESS);
}

Read input in reverse order

I am new to C. I have just learned pointers and struct.I am trying to modify the following program so that each student read is inserted at the front of the list of students, not at the end. How can I achieve it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE_LENGTH 80 // The longest line this program will accept
#define MAX_NUM_STUDENTS 500 // The maximum number of students this program can handle
#define MAX_NAME_SIZE 50 // The maximum allowable name length
typedef struct student_s Student;
struct student_s {
char name[MAX_NAME_SIZE];
int age;
Student* next; // Pointer to next student in a list
};
Student studentPool[MAX_NUM_STUDENTS]; // The student pool
int firstFree = 0;
Student* newStudent(const char* name, int age)
{
Student* student = NULL;
if (firstFree < MAX_NUM_STUDENTS) {
student = &studentPool[firstFree];
firstFree += 1;
strncpy(student->name, name, MAX_NAME_SIZE);
student->name[MAX_NAME_SIZE - 1] = '\0'; // Make sure it's terminated
student->age = age;
student->next = NULL;
}
return student;
}
Student* readOneStudent(FILE* file)
{
char buffer[MAX_LINE_LENGTH]; // Buffer into which we read a line from stdin
Student* student = NULL; // Pointer to a student record from the pool
// Read a line, extract name and age
char* inputLine = fgets(buffer, MAX_LINE_LENGTH, file);
if (inputLine != NULL) { // Proceed only if we read something
char* commaPos = strchr(buffer, ',');
if (commaPos != NULL) {
int age = atoi(commaPos + 1);
*commaPos = '\0'; // null-terminate the name
student = newStudent(buffer, age);
}
}
return student;
}
Student* readStudents(FILE *file)
{
Student* first = NULL; // Pointer to the first student in the list
Student* last = NULL; // Pointer to the last student in the list
Student* student = readOneStudent(file);
while (student != NULL) {
if (first == NULL) {
first = last = student; // Empty list case
} else {
last->next = student;
last = student;
}
student = readOneStudent(file);
}
return first;
}
void printOneStudent(Student student)
{
printf("%s (%d)\n", student.name, student.age);
}
void printStudents(const Student* student)
{
while (student != NULL) {
printOneStudent(*student);
student = student->next;
}
}
int main(void)
{
FILE* inputFile = fopen("studlist.txt", "r");
if (inputFile == NULL) {
fprintf(stderr, "File not found\n");
} else {
Student* studentList = readStudents(inputFile);
printStudents(studentList);
}
}
You currently have this code to insert at the end (of a non-empty list):
if (first == NULL) {
first = last = student; // Empty list case
} else {
last->next = student;
last = student;
}
To insert at the front of a non-empty list, you simply need to make the new student into the first student each time, by making its next pointer point to the current first student, and making the first pointer point at the new student.
if (first == NULL) {
first = last = student; // Empty list case
} else {
student->next = first;
first = student;
}
Draw the boxes; connect them with arrows. It should become obvious.
Also, you could simply use:
student->next = first;
first = student;
If first is null, student->next will be (re)set to null, so there's no need for a special case on first. Since last was only used within the function for adding to the end of the list, when inserting at the front, there's no need for last at all. These two observations make the code still simpler than the first version proposed.

C pointer does not get set after being passed to a function

So I have been working away learning C for a little while and I have finally hit a brick wall. I have found different practice problems online and I am really having problems with this one.
Initially I wrote this code all inside the main function and it works just fine and gives the desired output (Here is the example that is working)
#include <stdio.h>
#include <stdlib.h>
typedef struct Student STUDENT;
typedef struct Teacher TEACHER;
typedef struct Course COURSE;
int addStudentToTree(STUDENT *students, STUDENT *newStudent, STUDENT *currentStudent);
void printStudents(STUDENT *s);
struct Student
{
int StudentNumber;
char FirstName[BUFSIZ];
STUDENT *left, *right;
};
struct Teacher
{
int TeacherNumber;
char FirstName[BUFSIZ];
TEACHER *left, *right;
};
struct Course
{
int CourseNumber;
char CourseName[BUFSIZ];
int SemesterNumber;
COURSE *left, *right;
};
int main()
{
FILE *db = fopen("DatabaseFile.txt", "r");
char line[BUFSIZ];
STUDENT *newStudent, *currentStudent, *students;
students = NULL;
if (db != NULL)
{
while (fgets(line, sizeof(line), db) != NULL)
{
if (line[0] == 'S')
{
newStudent = malloc(sizeof(STUDENT));
if (sscanf(line, "S %d %s", &newStudent->StudentNumber, newStudent->FirstName) == 2)
{
newStudent->left = NULL;
newStudent->right = NULL;
if (students == NULL)
{
students = newStudent;
}
else
{
currentStudent = students;
while(currentStudent)
{
if (newStudent->StudentNumber != currentStudent->StudentNumber)
{
if (newStudent->StudentNumber < currentStudent->StudentNumber)
{
if (currentStudent->left == NULL)
{
currentStudent->left = newStudent;
break;
}
else
{
currentStudent = currentStudent->left;
}
}
else
{
if (currentStudent->right == NULL)
{
currentStudent->right = newStudent;
break;
}
else
{
currentStudent = currentStudent->right;
}
}
}
}
}
}
}
}
}
printStudents(students);
}
It successfully populates the tree and after that traverses it to give the following output:
Student Number: 203214 Student Name: Agneta
Student Number: 208214 Student Name: Janeta
Student Number: 213363 Student Name: Jill
Student Number: 215263 Student Name: Hansi
Student Number: 215363 Student Name: Laurent
Student Number: 228214 Student Name: James
Now part of the practice problem is also moving this out in to functions so everything doesn't just run inside the main method.
I have done this like so:
#include <stdio.h>
#include <stdlib.h>
typedef struct Student STUDENT;
typedef struct Teacher TEACHER;
typedef struct Course COURSE;
int addStudentToTree(STUDENT *students, STUDENT *newStudent, STUDENT *currentStudent);
void printStudents(STUDENT *s);
struct Student
{
int StudentNumber;
char FirstName[BUFSIZ];
STUDENT *left, *right;
};
struct Teacher
{
int TeacherNumber;
char FirstName[BUFSIZ];
TEACHER *left, *right;
};
struct Course
{
int CourseNumber;
char CourseName[BUFSIZ];
int SemesterNumber;
COURSE *left, *right;
};
int main()
{
FILE *db = fopen("DatabaseFile.txt", "r");
char line[BUFSIZ];
STUDENT *newStudent, *currentStudent, *students;
students = NULL;
if (db != NULL)
{
while (fgets(line, sizeof(line), db) != NULL)
{
if (line[0] == 'S')
{
newStudent = malloc(sizeof(STUDENT));
if (sscanf(line, "S %d %s", &newStudent->StudentNumber, newStudent->FirstName) == 2)
{
newStudent->left = NULL;
newStudent->right = NULL;
addStudentToTree(students, newStudent, currentStudent);
}
}
}
}
printStudents(students);
}
int addStudentToTree(STUDENT *students, STUDENT *newStudent, STUDENT *currentStudent)
{
if (students == NULL)
{
students = newStudent;
return 1;
}
else
{
currentStudent = students;
while(currentStudent)
{
if (newStudent->StudentNumber != currentStudent->StudentNumber)
{
if (newStudent->StudentNumber < currentStudent->StudentNumber)
{
if (currentStudent->left == NULL)
{
currentStudent->left = newStudent;
return 1;
}
else
{
currentStudent = currentStudent->left;
}
}
else
{
if (currentStudent->right == NULL)
{
currentStudent->right = newStudent;
return 1;
}
else
{
currentStudent = currentStudent->right;
}
}
}
}
}
return 0;
}
Now the problem arrises. I pass in the pointer 'students' and the first time it is passed it is a null pointer and is rightly caught by the if statement in the function. the newStudent variable points to a memory address.
After these line:
if (students == NULL)
{
students = newStudent;
return 1;
}
the pointer 'students' is now pointing at an actual address. But right after returning to the while loop in the main method, the 'students' pointer is once again a NULL pointer.
as an added info, you can see that putting in these printf's:
if (students == NULL)
{
printf("students: %p, newStudent: %p\n",students, newStudent );
students = newStudent;
printf("students: %p\n",students);
return 1;
}
produces this output:
students: 0x0, newStudent: 0x7fc6e2001200
students: 0x7fc6e2001200
students: 0x0, newStudent: 0x7fc6e2001800
students: 0x7fc6e2001800
students: 0x0, newStudent: 0x7fc6e2005200
students: 0x7fc6e2005200
students: 0x0, newStudent: 0x7fc6e2005800
students: 0x7fc6e2005800
students: 0x0, newStudent: 0x7fc6e2005e00
students: 0x7fc6e2005e00
students: 0x0, newStudent: 0x7fc6e2006400
students: 0x7fc6e2006400
I have really been spending a lot of time on this and finally gave in to come in here to ask you all.
Let me know if there is anything else you need to clarify this question.
Peter
you need to pass the address of the pointer... in this case the argument is created on the stack and when the function exits the stack is unwinded and your arguments remain no longer valid
int addStudentToTree(STUDENT **students, STUDENT *newStudent, STUDENT *currentStudent);
call like
addStudentToTree(&students,newStudent,currentStudent);
in the function do like
*sudents=NULL;
hope that helps
Imagine this:
void func(int var)
{
var = 1;
}
int main()
{
int var = 0;
func(var);
// What is the value of 'var' at this point?
...
}
If your answer to the question above is 1, then you should probably go back to the basics and learn the language from scratch.
If you do understand that the copy of variable var in function main retains its "original" value, then you shouldn't have any problems understanding that whatever value you assign to (the copy of) variable students inside function printStudents, will not take effect outside that function.
That being said, here are the general guidelines for passing this variable by reference:
Add * to the variable type in the function declaration - printStudents(STUDENT** s)
Add * to every reference that you make to this variable inside function printStudents
Add & before the variable in every place that you call the function - printStudents(&s)

Deleting in linked lists?

I wrote the following code:
#include<stdio.h>
struct student{
char name[25];
double gpa;
struct student *next;
};
struct student *list_head;
struct student *create_new_student(char nm[], double gpa)
{
struct student *st;
printf("\tcreating node\t");
printf("\nName=%s \t Gpa= %.2f\n", nm, gpa);
st = (struct student*)malloc(sizeof (struct student ));
strcpy(st->name, nm);
st->gpa = gpa;
st->next = NULL;
return st;
}
void printstudent(struct student *st)
{
printf("\nName %s,GPA %f\n", st->name, st->gpa);
}
void insert_first_list(struct student *new_node)
{
printf("\nInserting node: ");
printstudent(new_node);
new_node->next = list_head;
list_head = new_node;
}
struct student *delete_first_node()
{
struct student *deleted_node;
printf("\nDeleting node: ");
printstudent(deleted_node);
list_head = list_head->next;
return deleted_node;
}
void printlist(struct student *st)
{
printf("\nPrinting list: ");
while(st != NULL) {
printstudent(st);
st = st->next;
}
}
int main()
{
struct student *other;
list_head = create_new_student("Adil", 3.1);
other = create_new_student("Fatima", 3.8);
insert_first_list(other);
printlist(list_head);
other = delete_first_node();
printlist(list_head);
return 0;
}
When I run it, there are no errors or warnings. However, it stops at the delete part.The message says the program has stopped working.
Can you please help me find the problem?
In function delete_first_node the node deleted_node is not initialized and passed to function printstudent which try to access its member, results in undefined behavior.
The function should be
struct student *delete_first_node(){
struct student *deleted_node = list_head;
printf("\nDeleting node: ");
if(deleted_node != NULL)
{
printstudent(deleted_node);
list_head= list_head->next;
free(deleted_node);
}
else
printf("List is emty\n");
return list_head;
}

Resources