Edsel Norwood
Edsel Norwood

Reputation: 159

Scanf is being skipped after the first iteration of the for loop

When the for loop is run for the first time the program waits for the users input. But after the first time the two scanf lines seems to be skipped.

I've commented out the misc code:

#include <stdio.h>
int n = 0;
struct student {
    int age;
    char name[20];
};

void enterStudents() {
    printf("How many students do you want to enter? \n");
    scanf("%d", &n);
    struct student list[n];
    for(int i=0; i<n; i++){
        printf("Enter student number %d's age: ", i+1);
        scanf("%d", &list[i].age);
        printf("Enter student number %d's name: ", i+1);
        scanf(" %c", list[i].name);
        }
    listSort(list);
    }

/**int listSort(struct student list[n]) {
    char tempName[20];
    int tempAge;
    for(int i=0; i<n-1; i++){
        if(list[n].age < list[n+1].age) {
            tempAge = list[n].age;
            strcpy(tempName, list[n].name);
            list[n].age = list[n+1].age;
            strcpy(list[n].name, list[n+1].name);
            list[n+1].age = tempAge;
            strcpy(list[n+1].name, tempName);
        }
    }
}**/

int main() {
    enterStudents();

}

Upvotes: 1

Views: 727

Answers (2)

kashisha Jain
kashisha Jain

Reputation: 11

Whenever you take input from user where input has sequence as first is any other data type and then character or character array as in your case sequence is integer and then character. First you enter the age of student like 21 now when u press 'enter key' this 'enter' is taken as the input for next character input. Then your input will look like this:

  age=21
  name='enter' (enter key that you pressed)

Now we can use a function that is fflush(stdin), this will flush out that enter key and allow you to enter name. this code will work:

void enterStudents() 
{
    printf("How many students do you want to enter? \n");
    scanf("%d", &n);
    struct student list[n];
    for(int i=0; i<n; i++){
    printf("Enter student number %d's age: ", i+1);
    scanf("%d", &list[i].age);

    //enter this line after your first input
    fflush(stdin);

    printf("Enter student number %d's name: ", i+1);
    scanf(" %s", list[i].name);
    }
     listSort(list);
    }

Upvotes: 0

Pablo
Pablo

Reputation: 13590

One problem that beginners often overlook with scanf is that if the conversion fails or the the conversion converts less characters than the user entered (for example using %c instead of %s), then scanf leaves the not-converted characters in the input buffer. A subsequent call of scanf will first try to convert those characters that are in the input buffer, before it reads again from the user.

In your case you used %c but the user enters something that is longer than a single character. Only 1 character was used in the conversion the rest was left in the input buffer. In the next for iteration scanf("%d") tries to convert the characters that were left in the input buffer and because the user enter non-digits for the name, scanf fails and the next scanf reads only the first characters and left the rest behind, etc. That's why it appears that scanf skips the calls.

You should check the return value of scanf, it returns the number of successful conversion it made. This is great information, if you get less conversions than expected, then you know the scanf call failed and you can react to that. Also you may also clean the buffer if you are reading strings, newlines and word after an empty space are left in the buffer and that can cause some trouble with subsequent calls of scanf. You can use a function like this:

void clean_stdin(void)
{
    int c;
    while((c = getchar()) != '\n' && c != EOF);
}

So your function should look like this:

int enterStudents() {
    printf("How many students do you want to enter? \n");
    if(scanf("%d", &n) != 1)
    {
        fprintf(stderr, "Could not read from the user\n");
        return -1; // failure
    }

    if(n <= 0)
    {
        fprintf(stderr, "invalid number of students\n");
        return -1;
    }

    struct student list[n];
    for(int i=0; i<n; i++){
        printf("Enter student number %d's age: ", i+1);
        if(scanf("%d", &list[i].age) != 1)
        {
            fprintf(stderr, "could not read age from the user\n");
            return -1;
        }

        printf("Enter student number %d's name: ", i+1);
        if(scanf("%19s", list[i].name) != 1)
        {
            fprintf(stderr, "could not read name from the user\n");
            return -1;
        }

        // cleaning stdin
        clean_stdin();
    }

    ...
    return n; // sucess
}

Note that I've changed the function so that it returns -1 on failure and the number of students read on success, so the caller can know that something went wrong and get the number of students at the same time. In general, in order to make your code more robust, you should never trust the user and you should double check user input. You have to check that the user didn't enter a negative number for the student count. The "correct" mind set is "the user is trying to break your code by entering incorrect data and I have to deal with that". I know, the code become slightly more larger, but it is more robust and if something fails, you can narrow down more quickly (based on the error messages) where something went wrong. In your code, when something fails, you have really no idea where it could have happened.

Also note that in the name scan I used scanf("%19s", ..) instead of %s. The reason is that using %s you might overflow the buffer if the name is longer than the buffer can hold. If the name is longer than 19 characters, it will overflow the buffer. With "%19s" you are limiting how many characters should be read, in this case scanf will convert at most 19 characters and ignore the rest. Why 19 and not 20? You have to use 19, because strings in C are '\0'-terminated, so the last space in the array should be used for the '\0'-terminating bytes, hence 19.

Upvotes: 1

Related Questions