Im using a double linked list to make a studentdata base, however im having trouble trying to get all my scanfs and fgets to actually work, after scanf(lastname) is declared it freezes, instead of going on to next printf statement asking for the classification. I added my .h file code therefore if oyu could let me know i made a error. I tried using a function to clear the stdin
//.h file
#ifndef STUDENT_H_
#define STUDENT_H_
typedef struct student{
char* name;
char* lastname;
long studentID;
char* class;
int grad_Year;
struct student *next;
struct student *prev;
}student_t;
typedef struct DLL{
//struct student *next;
//struct student *prev;
struct student* head; //pointer to head node
struct student* tail; //pointer to tail
}DLL_t;
#endif /* STUDENT_H_ */
#ifndef STUDENT_C_
#define STUDENT_C_
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include "Student.h"
#define BUFFERSIZE 128
//typedef struct student{
//
// char* name;
// char* lastname;
// long studentID;
// char* class;
// int grad_Year;
//
//};
//Global Var
typedef struct student studentNode; //creates new struct studentNode with student attrigutes
typedef studentNode *StudentPtr; //creates studentptr
typedef struct DLL dllNext;
typedef dllNext *next;
//Prototypes
void instructions(void){
printf( "Enter your choice:\n"
" 1 to insert an element into the list.\n"
" 2 to delete an element from the list.\n"
" 3 to end.\n" );
}
void insert(studentNode **headList,char* name, char* lastname, char* class, long studentID,int grad_Year){
//starters
StudentPtr newPtr;
StudentPtr prevPtr;
StudentPtr currentPtr;
newPtr = ( studentNode * ) malloc( sizeof( studentNode ) );
if(newPtr != NULL){
newPtr->name = name;
newPtr->lastname = lastname;
newPtr->class = class;
newPtr->studentID = studentID;
newPtr->grad_Year = grad_Year;
newPtr->next = NULL;
prevPtr = NULL;
currentPtr = *headList;
if(currentPtr != NULL){
prevPtr = currentPtr;
currentPtr=currentPtr->next;
}
if(prevPtr == NULL){
newPtr->next = *headList;
*headList = newPtr;
}
else{
prevPtr->next = newPtr;
newPtr->next = currentPtr;
}
}
}
void delete(){
printf("In delete function");
}
void clean_file_buffer(FILE *fp)
{
int c;
while((c = fgetc(fp)) != '\n' && c!=EOF);
}
int main(int argc, char *argv[]){
int option;
StudentPtr newPtr;
StudentPtr head = NULL;
instructions();
printf("your option: ",&option);
scanf("%d",&option);
while(option != 3){
switch(option){
case 1:
//insert(&name,&lastname,&class);
//printf("insert\n");
printf("Enter first name: ");
fgets(newPtr->name,BUFFERSIZE,stdin);
newPtr->name[strcspn (newPtr->name, "\n")] = 0;
//scanf("%s",newPtr->name);
//fgets(newPtr->name,BUFFERSIZE,stdin);
//printf("Name is %s\n", newPtr->name);
printf("Enter last name: ");
fgets(newPtr->lastname,BUFFERSIZE,stdin);
newPtr->lastname[strcspn (newPtr->lastname, "\n")] = 0;
//scanf("%s",newPtr->lastname);
//printf("Last name is\n %s",newPtr->lastname);
printf("Enter class: ");
scanf("%s",newPtr->class);
//fgets(newPtr->class,BUFFERSIZE,stdin);
// printf("Enter StudentID");
// scanf("%ld",newPtr->studentID);
//
// printf("Enter Graduation Year");
// scanf("%d", newPtr->grad_Year);
insert(&head,newPtr->name,newPtr->lastname, newPtr->class, newPtr->studentID, newPtr->grad_Year);
break;
default:
printf("Invalid choice.\n\n");
instructions();
break;
}
}
}
#endif /* STUDENT_C_ */
At first: this part of your code
int option;
StudentPtr newPtr;
StudentPtr head = NULL;
instructions();
printf("your option: ", &option);
scanf("%d", &option);
while (option != 3)
{ //...
has problems:
dll.c(96,29): warning C4474: 'printf' : too many arguments passed for format string
dll.c(96,29): message : placeholders and their parameters expect 0 variadic arguments, \
but 1 were provided
dll.c(108): error C4700: uninitialized local variable 'newPtr' used
option
and newPtr
are not. Even if your compiler tolerates it. But in this case option
has an unknown value and you try to use it as condition in a loop.typedef
pointers. Is is a shot in your own foot. Even if you use a Ptr
suffix, or a P or whatever. C
has it covered since the beginning: use an asterisk. It is known by everybody, compiler included, that if Student
is a struct
Student*
is a pointer to it. And Student**
is a pointer to a pointer to it, and so on.typedef struct student
studentNode; // creates new struct studentNode with
// student attrigutes
typedef studentNode* StudentPtr; // creates studentptr
typedef struct DLL dllNext;
typedef dllNext* next;
How would someone know, even you tomorrow, that next
is a pointer to dllNext
?
// Global Var
About this comment and the following lines in your code: Do not use globals. Never.
About your question: scanf()
does not freeze at all. The "problem" is that scanf()
does not consume the \n
that ends the input. And the system can not decide that this \n
is garbage or if it should be somewhat flushed, since no one can be sure it is. It is up do the program logic. And can be tons of things typed by the user and not read yet...
scanf()
behaviour: an exampleWriting programs are a certified way to learn to program. But also are to read the manuals. scanf()
returns an int
. ALWAYS test it. scanf()
is a scanner. A very smart one. scan formatted input is the objective of scanf
, that is the origin of the name. It is great for consuming tabular data, things like csv
files. keyboard is not formatted input, so when you scanf
on stdin
you must be prepared for surprises...
And how to be prepared?
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer[30] = {0};
int res = scanf("%10s", buffer);
printf("scanf() returned %d. size of string read is %d\n",
res, strlen(buffer));
if ( res != 1 ) return -1;
printf(
"now trying to read a char. Would block the "
"program?\n");
int c = fgetc(stdin);
printf("Read something. Code of char is %d\n", c);
return 0;
}
This program tries to read a 10-char string. If scanf()
is able to read something it returns 1
since the call was to scan for exactly ONE string
.
Here is the output when someone enters "12345" and press ENTER:
12345
scanf() returned 1. size of string read is 5
now trying to read a char. Would block the program?
Read something. Code of char is 10
As you see it by the prompt "now trying..." the program goes on and the call to fgetc()
read a char
with value 10
a.k.a the \n
that ended the call to scanf
.
This is the way it works.
Look at the output here for a 2nd run of the program:
1234512345Agfkdjghkfdghfkdgdkfhgfdkjghfd
scanf() returned 1. size of string read is 10
now trying to read a char. Would block the program?
Read something. Code of char is 65
scanf()
returns as soon as it reads the 10 asked-for char'. The
fgetc()takes the
A,
65in
ASCII`. But the rest stays there to be read.
[a bit off-topic here]
a linked list is a list of nodes
. It is not a list of student
or anything. Each linked node points to --- or contains --- some data. And in your program it is the Student
.
List
with the data. Never. Why? This is the way to write the code for linked list only once. And use ever.main
function inside the same file that contains the implementation of the list#ifndef student_h_
#define student_h_
typedef struct
{
char* name;
char* lastname;
long id;
char* class;
int grad_year;
} Student;
typedef struct st_node
{
struct st_node* next;
struct st_node* prev;
Student S;
//Student* S; could be better
} Node;
typedef struct
{
size_t size;
node* head;
node* tail;
} List;
#endif /* student_h_ */
Here you see
List
has a size, and a List
can for sure be empty.List
does not mention Student
at allvoid insert(
studentNode** headList, char* name, char* lastname,
char* class, long studentID, int grad_Year)
StudentPtr newPtr;
StudentPtr prevPtr;
StudentPtr currentPtr;
// ...
You want to insert a Student
in the List
. The ideal way should be generic, but see this one
int insert(Student* s, List* l);
This is what you want:
insert
. This is encapsulation. The way you did if you add a field to Student
you will need to change every insert
call.void
. Return status, return the size of the list, anything. void
is a waste.I will not provide a full implementation since it is a bit off-topic but I can provide one case you need. Just comment below.