cscanffgets

Scanf freezes after second inputs


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_ */

Solution

  • 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
    
    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.

    Scanf freezes after second inputs

    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...

    looking for scanf() behaviour: an example

    Writing 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?

    an example

    #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 theA, 65inASCII`. But the rest stays there to be read.

    A note about your code

    [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.

    an example of your header, changed

    #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

    from your code

    void 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:

    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.