Read input in reverse order - c

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.

Related

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);
}

Struct program takes input in different format

I am studing for a test and have this question : Modify this program so that it takes input in a different format:each line consists of an age, a comma, a space and a name, e.g.
23, Angus McGurkinshaw
I understand I need to modify something in readOneStudent function.Not sure how to read the name by knowing the address of comma.Please help.
The input and output should look like this:
input = 21, Fred Nurk
927, Arwen Evensong
output is suppose to be:
Arwen Evensong (927)
Fred Nurk (21)
..
#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
};
// Create a pool of student records to be allocated on demand
Student studentPool[MAX_NUM_STUDENTS]; // The student pool
int firstFree = 0;
// Return a pointer to a new student record from the pool, after
// filling in the provided name and age fields. Returns NULL if
// the student pool is exhausted.
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;
}
// Read a single student from a csv input file with student name in first column,
// and student age in second.
// Returns: A pointer to a Student record, or NULL if EOF or an invalid
// student record is read. Blank lines, or lines in which the name is
// longer than the provided name buffer, or there is no comma in the line
// are considered invalid.
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 {
student -> next = first;
first = student;
}
student = readOneStudent(file);
}
return first;
}
// printOneStudent: prints a single student, passed by value
void printOneStudent(Student student)
{
printf("%s (%d)\n", student.name, student.age);
}
// printStudents: print all students in a list of students, passed
// by reference
void printStudents(const Student* student)
{
while (student != NULL) {
printOneStudent(*student);
student = student->next;
}
}
int main(void)
{
FILE* inputFile = stdin;
if (inputFile == NULL) {
fprintf(stderr, "File not found\n");
} else {
Student* studentList = readStudents(inputFile);
printStudents(studentList);
}
}
Below code should work.
Note that
strchr()
This returns a pointer to the first occurrence of the character c in the string str, or NULL if the character is not found.
I think you were trying to get age from name string.
#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
};
// Create a pool of student records to be allocated on demand
Student studentPool[MAX_NUM_STUDENTS]; // The student pool
int firstFree = 0;
// Return a pointer to a new student record from the pool, after
// filling in the provided name and age fields. Returns NULL if
// the student pool is exhausted.
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;
}
// Read a single student from a csv input file with student name in first column,
// and student age in second.
// Returns: A pointer to a Student record, or NULL if EOF or an invalid
// student record is read. Blank lines, or lines in which the name is
// longer than the provided name buffer, or there is no comma in the line
// are considered invalid.
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);
//printf("age and commaPos is %d,%s \n ",age,commaPos);
char* name = commaPos+1;
name[strcspn(name, "\n")] = 0; //remove /n from fgets
*commaPos = '\0'; // null-terminate the age
int age = atoi(buffer);
//printf("age and commaPos is %d,%s \n ",age,name);
//student = newStudent(buffer, age);
student = newStudent(name, 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 {
student -> next = first;
first = student;
}
student = readOneStudent(file);
}
return first;
}
// printOneStudent: prints a single student, passed by value
void printOneStudent(Student student)
{
printf("%s (%d)\n", student.name, student.age);
}
// printStudents: print all students in a list of students, passed
// by reference
void printStudents(const Student* student)
{
while (student != NULL) {
printOneStudent(*student);
student = student->next;
}
}
int main(void)
{
FILE* inputFile = stdin;
if (inputFile == NULL) {
fprintf(stderr, "File not found\n");
} else {
Student* studentList = readStudents(inputFile);
printStudents(studentList);
}
}

pointers and Structs from csv file

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.

Changing calls within a Struct

Given the struct I have created below, how would I change the code to make it read from insert (student) rather than from the way it is now. Basically, the code at the moment takes input as a student written as:
student guy,23
and the output will be:
student guy (23)
and this part is working thus far. What the insert (and comes before) code will do is when multiple students are entered, sort them in alphabetical order - or if they have an identical name, by age. I have completed the code to do this but cannot seem to work out how to call it properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <assert.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
// The declaration of the student record (or struct). Note that
// the struct contains the name as an array of characters, rather than
// containing just a pointer to the name as before.
typedef struct student_s Student;
struct student_s {
char name[MAX_NAME_SIZE];
int age;
Student* next; // Pointer to next student in a list
};
bool comesBefore(const Student* student1, const Student* student2) {
int name_compare = strcmp(student1->name, student2->name);
if (name_compare < 0) {
return true;
}
else if (name_compare == 0) {
int age1 = student1->age;
int age2 = student2->age;
if (age1 < age2) {
return true;
}
}
return false;
}
Student* insert(Student* student, Student* list) {
Student* curr = NULL;
Student* prev = NULL;
if (list == NULL)
return student;
if (comesBefore(student, list)) {
student->next = list;
return student;
}
for (curr = list, prev = NULL;
curr != NULL && comesBefore(student, curr) != true;
prev = curr, curr = curr->next);
assert(prev != NULL);
student->next = curr;
prev->next = student;
return list;
}
// Create a pool of student records to be allocated on demand
Student studentPool[MAX_NUM_STUDENTS]; // The student pool
int firstFree = 0;
// Return a pointer to a new student record from the pool, after
// filling in the provided name and age fields. Returns NULL if
// the student pool is exhausted.
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;
}
// Read a single student from a csv input file with student name in first column,
// and student age in second.
// Returns: A pointer to a Student record, or NULL if EOF or an invalid
// student record is read. Blank lines, or lines in which the name is
// longer than the provided name buffer, or there is no comma in the line
// are considered invalid.
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* cp = fgets(buffer, MAX_LINE_LENGTH, file);
if (cp != 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;
}
// Reads a list of students from a given file. Input stops when
// a blank line is read, or an EOF occurs, or an illegal input
// line is encountered.
// Returns a pointer to the first student in the list or NULL if no
// valid student records could be read.
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;
}
// printOneStudent: prints a single student, passed by value
void printOneStudent(Student student)
{
printf("%s (%d)\n", student.name, student.age);
}
// printStudents: print all students in a list of students, passed
// by reference
void printStudents(const Student* student)
{
while (student != NULL) {
printOneStudent(*student);
student = student->next;
}
}
// Main program. Read a linked list of students from a csv file, then display
// the contents of that list.
int main(void)
{
FILE* inputFile = stdin;
if (inputFile == NULL) {
fprintf(stderr, "File not found\n");
}
else {
Student* studentList = readStudents(inputFile);
printStudents(studentList);
// The program could now do various things that make use of
// the linked list, like deleting students and adding new ones,
// but the program is already quite long enough!
}
}
I believe it requires some sort of edit to readStudents but cannot work out what call change to make regardless of source material I have read.
The key idea is that you need to adjust this:
if (first == NULL) {
first = last = student; // Empty list case
}
else {
last->next = student;
last = student;
}
At present you are putting the new student at the end of the list. Instead you are going to keep the list in a sorted order. In concept you have
Arthur -> Bill -> Dave
and a new record, Charles. So you compare first with Charles, discover Charles is bigger, so go on to Bill, and then eventually hit Dave and now know to insert Charles after Bill and before Dave.
Now have a look at the insert() function. Can you see that this is what it's doing? Walking the list and eventually inserting the record - note how it adjusts the "Bill" to point to the new record, and have the new record point to Dave.

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)

Resources