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)
Related
The function display does not print the id of a student, but some number. The value of pointers are assigned correctly and when I print them, they are as they should be. But I cannot print the id of the Student. Could you please help me with the problem?
I am getting this as output.
Enter Student ID
23
New Student Created
The id of student at position 1 is 12672416
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void displayList();
void insertAtFirst();
struct Student * createStudentNode();
struct Student {
int id;
struct Student * nextStudent;
};
struct Student * startStudent;
int main(int argc, char * argv[]) {
insertAtFirst();
displayList();
return 0;
}
void displayList() {
int x = 1;
struct Student * tempStudent = (struct Student * ) malloc(sizeof(struct Student * ));
if (startStudent == NULL) {
printf("The list is empty.\n");
} else {
tempStudent = startStudent;
while (tempStudent != NULL) {
printf("The id of student at position %d is %d\n", x, tempStudent -> id);
tempStudent = tempStudent -> nextStudent;
x++;
}
}
}
void insertAtFirst() {
struct Student * t;
struct Student * tempStudent;
tempStudent = createStudentNode();
printf("Enter Student ID\n");
scanf("%d", tempStudent -> id);
printf("New Student Created\n");
tempStudent -> nextStudent = NULL;
if (startStudent == NULL) {
startStudent = tempStudent;
} else {
tempStudent -> nextStudent = startStudent;
startStudent = tempStudent;
}
}
//This method creates a new Student structure and returns it address
struct Student * createStudentNode() {
struct Student * n;
n = (struct Student * ) malloc(sizeof(struct Student));
n -> nextStudent = NULL;
return n;
}
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.
I need to read-in a .csv file and print it's contents, the students' names and age. The actual output is correct but it is appended with junk characters. The program also terminates with a segmentation fault! For some reason, these problems do not occur on OSX but do on Windows.
Code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINE_LENGTH 80
#define MAX_NUM_STUDENTS 500
#define MAX_NAME_SIZE 50
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];
Student* student = NULL;
char* inputLine = fgets(buffer, MAX_LINE_LENGTH, file);
if (inputLine != NULL) { // Proceed only if we read something
char* commaPos = strchr(buffer, ',');
if (commaPos != NULL && commaPos > buffer) {
int age = atoi(commaPos + 1);
*commaPos = '\0'; // null-terminate the name
student = newStudent(buffer, age);
}
}
return student;
}
int precedes(const Student* new, const Student* old)
{
int final = strcmp(old->name, new->name);
if (final == 0) {
if (old->age <= new->age) {
final = -1;
} else {
final = 1;
}
}
return final;
}
Student* insert(Student* student, Student* list)
{
Student* current = list;
if (list == NULL) {
student->next = list;
list = student;
} else if (precedes(current, student) < 0) {
student->next = current;
current = student;
list = current;
} else {
while(current->next && precedes(student, current->next) < 0) {
current = current->next;
}
student->next = current->next;
current->next = student;
}
return list;
}
Student* readStudents(FILE *file)
{
Student* student_list;
Student* student = readOneStudent(file);
while (student != NULL) {
student_list = insert(student, student_list);
student = readOneStudent(file);
}
return student_list;
}
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);
}
}
input:
Zaphod Beeblebrox,250
Albert Einstein,133
Albert Einstein,7
The output is sorted alphabetically for student names' then increasing age.
expected output:
Albert Einstein (7)
Albert Einstein (133)
Zaphod Beeblebrox (250)
on Windows, output is appended with:
p
# (0)
then segfault.
student_list is a uninitialized stack variable. It could be 0 (NULL) on some machines but that does not have to be true. In general you should expect an initialized stack variable to contain whatever garbage is in that location of memory.
Here is the simple fix:
Student* readStudents(FILE *file)
{
Student* student_list = NULL;
Student* student = readOneStudent(file);
while (student != NULL) {
student_list = insert(student, student_list);
student = readOneStudent(file);
}
return student_list;
}
The bug is triggered in the insertion method here:
if (list == NULL) {
student->next = list;
list = student;
}
Whatever the variable list is pointing to will be used for sorting. In your case it looks like it was "bigger" than all the legit inputs and garbage made its way to the back of your linked list. The print function would then keep dereferencing uninitialized next pointers until the SIGSEGV is triggered.
For debugging this sort of problems you might want to check out valgrind.
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.
I am trying to implement a hash table with linked list chaining. The following code below works -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABSIZ 200
struct record {
struct record *next;
char name[BUFSIZ];
int data;
};
static struct record *htable[TABSIZ];
unsigned hash(char *s)
{
unsigned h;
for (h = 0; *s; s++)
h = *s;
//printf("%d", h%TABSIZ);
//I know its not a good hash function but i wanted to check chaining
return h % TABSIZ;
}
struct record *find(char *name)
{
struct record *item;
for (item = htable[hash(name)]; item; item = item->next)
{
if (strcmp(name, item->name) == 0)
return item;
}
return NULL;
}
struct record *insert(char *name,int value)
{
struct record *item;
unsigned h;
if ((item = find(name)) == NULL)
{
if ((item = malloc(sizeof (*item))) == NULL)
return NULL;
strcpy(item->name, name);
item->data=value;
h = hash(name);
item->next = htable[h];
htable[h] = item;
}
return item;
}
void printTable()
{
int i=0;
struct record *temp;
for(i=0;i<=TABSIZ;i++)
{
temp=htable[i];
while(temp!=NULL)
{
printf("\n%d - %s - %d\n", i,temp->name, temp->data);
temp=temp->next;
}
}
}
int main(void)
{
char buf[BUFSIZ];int value;
struct record *item;
do{
printf("Enter the name of the student:\n");
scanf("%s", buf);
if(strcmp(buf,"stop")==0) break;
printf("Enter the marks of the student:\n");
scanf("%d", &value);
if(insert(buf, value)==NULL)
{
break;
}
}while((strcmp(buf,"stop"))!=0);
printf("Enter a name to find: ");
scanf("%s", buf);
if((item=find(buf))!=NULL)
printf("The marks of the student is %d\n", item->data);
else printf("\n Not Found\n");
printTable();
return 0;
}
Now I am trying to remove the global variable and use local variable for the array of structures. I removed the global declaration of htable and declared it in main as
struct record *htable[TABSIZ];
and changed the functions to
struct record *find(struct record *htable, char *name);
struct record *insert(struct record *htable, char *name,int value);
and I'm calling the functions as
find(htable, name);
insert(htable,name,value);
but now my program is segfaulting. Am i passing the array of structures right? and have I declared it correctly. Any help would be greatly appreciated.
I was going down the wrong path with my earlier answer.
When it's a global, it's automatically initialized to 0.
When it's declared on the stack of main, it's not initialized.
Add a memset( htable, 0, sizeof(htable)) in main(), and that should return it to the previous behavior.
In printTable():
for(i=0;i<=TABSIZ;i++)
looks suspect. You prabably want:
void printTable()
{
unsigned int i;
struct record *temp;
for(i=0; i < TABSIZ;i++)
{
for (temp=htable[i]; temp!=NULL; temp=temp->next )
{
printf("\n%d - %s - %d\n", i,temp->name, temp->data);
}
}
}