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.
Related
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);
}
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);
}
}
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.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I am trying to append at the end of the list and return head at the end of this function.
This is a singly linked list. So, I have lost my head after traversing to the end.
file.c
#define NAME_LEN 30
struct equipment{
char type[NAME_LEN+1];
char description[NAME_LEN+1];
int quantity;
struct equipment *next;
};
struct equipment *append_to_list(struct equipment *list){
char type[NAME_LEN + 1], description[NAME_LEN + 1];
int quantity;
printf("Enter equipment type: ");
fgets(type, NAME_LEN, stdin);
printf("Enter description of the equipment: ");
fgets(description, NAME_LEN, stdin);
printf("Enter quantity: ");
scanf("%d", &quantity);
struct equipment *temp = (struct equipment *)malloc(sizeof(struct equipment));
strcpy(temp->type, type);
strcpy(temp->description, description);
temp->quantity = quantity;
temp->next = NULL;
bool doesExist = false;
if ( list == NULL ){
list = temp;
}
else{
while ( list->next != NULL ){
if ( list == temp ){
printf("This equipment is already in the list\n");
}
list = list->next;
}
list->next = temp;
}
// return head of this list here;
}
In this int main function, e_list should point to head of the linked list after calling append_to_list(e_list).
int main(void)
{
struct equipment *e_list = NULL;
e_list = append_to_list(e_list);
}
How do I create a reference and traverse that dummy head? That way I never lose my original head.
You need a temporary variable, in the else add
struct equipment *tmp;
tmp = list;
And replace all occurrences of list in the else. At the end return you list variable.
Since you are already putting the newly created structure temp at the end. Your head will not change between different calls in main because the head is at the opposite side of the end.
You need a static variable to keep track of head. Here is the tested code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define NAME_LEN 30
struct equipment
{
char type[NAME_LEN + 1];
char description[NAME_LEN + 1];
int quantity;
struct equipment *next;
};
struct equipment *
append_to_list (struct equipment *list)
{
char type[NAME_LEN + 1], description[NAME_LEN + 1];
int quantity;
printf ("Enter equipment type: ");
fgets (type, NAME_LEN, stdin);
printf ("Enter description of the equipment: ");
fgets (description, NAME_LEN, stdin);
printf ("Enter quantity: ");
scanf ("%d", &quantity);
struct equipment *temp =
(struct equipment *) malloc (sizeof (struct equipment));
strcpy (temp->type, type);
strcpy (temp->description, description);
temp->quantity = quantity;
temp->next = NULL;
bool doesExist = false;
static struct equipment * head = NULL; //New variable
if (list == NULL)
{
list = temp;
head = list;
}
else
{
while (list->next != NULL)
{
if (list == temp)
{
printf ("This equipment is already in the list\n");
}
list = list->next;
}
list->next = temp;
}
return head; //return head here
}
int main()
{
struct equipment *e_list = NULL;
e_list = append_to_list(e_list);
e_list = append_to_list(e_list);
e_list = append_to_list(e_list);
return 0;
}
You really could do a few things here.
Firstly though, your code is rather unsafe. Your buffers are far too small, don't use scanf to convert an integer (it doesn't check for failure), and don't use strcpy (especially when your buffers are tiny). Also, the calls to printf are a bit pointless if you're just using a literal string - puts() should be considered instead - and don't cast the return from malloc(). This is maybe a bit safer.
#include <bsd/bsd.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define NAME_LEN 32
#define nputs(STR) fputs((STR), stdout)
struct equipment {
char type[NAME_LEN];
char description[NAME_LEN];
int quantity;
struct equipment *next;
};
struct equipment *
append_to_list(struct equipment *list)
{
char type[BUFSIZ], description[BUFSIZ], quantity_string[BUFSIZ];
int quantity;
nputs("Enter equipment type: ");
fgets(type, BUFSIZ, stdin);
nputs("Enter description of the equipment: ");
fgets(description, BUFSIZ, stdin);
nputs("Enter quantity: ");
fgets(quantity_string, BUFSIZ, stdin);
char *endptr;
quantity = strtol(quantity_string, &endptr, 10);
if (quantity_string == endptr) {
fprintf(stderr, "Error: invalid integer input '%s'\n", quantity_string);
exit(1);
}
struct equipment *temp = malloc(sizeof *temp);
strlcpy(temp->type, type, NAME_LEN);
strlcpy(temp->description, description, NAME_LEN);
temp->quantity = quantity;
temp->next = NULL;
bool doesExist = false;
if (list == NULL) {
list = temp;
} else {
while (list->next != NULL) {
if (list == temp)
puts("This equipment is already in the list");
list = list->next;
}
list->next = temp;
}
// return head of this list here;
}
As to keeping track of the head, you could use a different function to initialize the tree than to append to it, thus giving you a slightly unique root object. You could add a field to each struct that stores the location of the root, or you could make a separate struct entirely that holds the root and perhaps some metadata about it. There are plenty of options. I agree with a few of the comments above, it isn't entirely clear what you're asking for here.