بسمة صلاح
بسمة صلاح

Reputation: 95

Why we need getchar() although using scanf()?

this code should read a positive number and if user enter a non-numeric value, it asking him again to enter a number and wait the input to check again till entering a number

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) ) ;

but actually when entering non-numeric it keeps doing the body of the while loop without reading the waiting to check the condition of the while loop while(scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0)

when edit the condition to

int clean_stdin()
{
    while (getchar()!='\n');
    return 1;
}

 do
   {
        printf("Enter a positive number: ");
   }
  while ( (scanf("%d%c", &n,&c) != 2 || c!='\n' || n<0) && clean_stdin() ) ;

It executes in the right way, but I don't understand why we need to add getchar() although we already use scanf() in the condition

Upvotes: 1

Views: 2958

Answers (4)

chux
chux

Reputation: 153457

When scanf("%d%c", ... encounters non-numeric input, the "%d" causes the scanning to stop and the offending character to remain in stdin for the next input function. The "%c" does not get a chance to read that non-numeric character.

If code re-reads stdin with the same scanf("%d%c", ..., the same result. Some other way is needed to remove the non-numeric input. getchar(), getch(), etc. will read any 1 character.

Example code GetPositiveNumber()

Upvotes: 3

mattn
mattn

Reputation: 7723

See this code

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

If you type over two or three characters, second call of scanf doesn't work as interructive. And getch() is not standard. Also you shouldn't call getchar() since it may block. You can use fseek.

Hack using fseek works only on Windows. :-(

BAD SOLUTION

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  fseek(stdin, 0, SEEK_END);
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

GOOD SOLUTION

#include <stdio.h>

int
main(int argc, char* argv[]) {
  char buf[BUFSIZ];
  char c = 0;
  scanf("%c", &c);
  printf("%c\n", c);
  while (!feof(stdin) && getchar() != '\n');
  scanf("%c", &c);
  printf("%c\n", c);
  return 0;
}

This works good on Windows & Linux

Upvotes: 0

xing
xing

Reputation: 2508

Consider using fgets to collect input. Any invalid input is already removed from the input stream making it simpler to try again.
Parse the input with sscanf or others as needed.

char input[40] = "";
int valid = 0;
int n = 0;

do {
    printf ( "Enter a positive integer\n");
    if ( fgets ( input, sizeof ( input), stdin)) {
        int last = 0;
        if ( 1 == ( valid = sscanf ( input, "%d%n", &n, &last))) {
            if ( n < 0  || input[last] != '\n') {
                valid = 0;//reset if negative or last is not newline
            }
        }
    }
    else {
        fprintf ( stderr, "problem getting input from fgets\n");
        return 0;
    }
} while ( valid != 1);

Upvotes: 2

Iharob Al Asimi
Iharob Al Asimi

Reputation: 53006

Because scanf() with most specifiers ignores white spaces, and you need to collect them before calling scanf() again if a '\n' is left in the buffer. If you don't collect them (and discard them immediately), then scanf() will return immediately on the next call.

Upvotes: 0

Related Questions