5

In line 5 I read an integer and isint is getting 1 if it reads an integer or 0 if it's not an integer. If isint is 0 I have a loop asking user to give an integer and I read until the user gives an integer. I try this code giving a character instead of an integer but I have an infinite loop. The program just doesn't wait to give a new input. What's wrong with my code?

#include <stdio.h>

int main(void) {

  int arg1;
  //int arg2;
  int attacknum = 1;
  int isint = 1;

  //printf("Insert argument attacks and press 0 when you have done this.\n");
  printf("Attack %d\n", attacknum);
  attacknum++;
  printf("Give attacking argument:");
  isint = scanf("%d", &arg1);  //line 5

  while(isint == 0){
    printf("You did not enter a number. Please enter an argument's number\n");
    isint = scanf("%d", &arg1);
    printf("is int is %d\n", isint);
  }
  return 0;
}
Jens
  • 69,818
  • 15
  • 125
  • 179
Dchris
  • 2,867
  • 10
  • 42
  • 73

2 Answers2

13

As others have mentioned, if scanf can't parse the input, it leaves it unscanned.

Generally scanf is a poor choice for interactive input because of this kind of behavior, and because it doesn't match the line-at-a-time interface experienced by the user.

You are better off reading one line into a buffer using fgets. Then parse that line using sscanf. If you don't like the input, throw the whole line away and read another one.

Something like this:

#include <stdio.h>

int main(void)
{
  char line[256];

  int arg1;
  int isint;

  while (1) {
    printf("Give attacking argument:");
    fgets(line, sizeof line, stdin);
    isint = sscanf(line, "%d",&arg1);
    if (isint) break;

    printf("You did not enter a number.Please enter an argument's number\n");
  }

  printf("Thanks for entering %d\n", arg1);

  return 0;
}

(For production code you'll want to handle long lines, check return codes, also check for trailing garbage after the number, etc.)

Actually, an even better approach would be to not use scanf if you just want to read an integer, and instead use strtol. That gives you a handy pointer to the character just after the number, and you can check that it's whitespace or nul.

poolie
  • 9,289
  • 1
  • 47
  • 74
  • 1
    This will still accept inputs like `123ab` as `123`. It's good idea to use a for loop to search for invalid characters inside line, see the same question here: http://stackoverflow.com/questions/20829672/why-this-example-is-stuck-in-an-infinite-loop-in-c – ilgaar Dec 31 '13 at 13:05
  • Detecting whether there is trailing garbage is a good idea. Doing it by manually scanning the string, as in that question, is a very long and inelegant way to do it. Better to just use [`strtol`](http://man7.org/linux/man-pages/man3/strtol.3.html) which gives you a pointer to the following character. – poolie Jan 03 '14 at 19:24
4

When scanf is confronted with a non-digit it will not consume any input and return that zero integers were read. The non-digit will stay in the input for the next call to scanf that will behave the same as the first call, etc.

In answer to your question below. You could use fgetc to parse at least one character, but this will give the error messages for every character already typed. Typically I think you want to skip until a newline. To this end you could use fgets as suggested by poolie. Or you could add the following after scanf.

int ch;
if (isint == 0)
while ((ch = fgetc(stdin)) != EOF && ch != '\n')
{
     /* Skip characters */
}

P.S: In your case it is probably better to put it just before the first printf in the loop.

Community
  • 1
  • 1
Bryan Olivier
  • 5,207
  • 2
  • 16
  • 18
  • what do you suggest to solve the problem?how can i consume the non-digit? – Dchris Jun 02 '13 at 12:04
  • @Dchris I added some suggestions. – Bryan Olivier Jun 02 '13 at 12:16
  • 1
    @Dchris Which research have you done? Are you serious with that question? Why aren't you reading a book? – autistic Jun 02 '13 at 13:25
  • @poolie: And others with edit happy fingers: thanks for fixing the variable name, but you could also have made a comment. Beware however that this coding style is intentional, so please don't change it. – Bryan Olivier Jun 04 '13 at 09:03
  • I could have left a comment but editing seems simpler, makes less noise, and seems in the spirit of SO's wiki nature. And that coding style... ok, you can choose, but it's pretty unusual, can you blame me for thinking it was a typo too? [No need to be rude.](http://stackoverflow.com/posts/16882518/revisions) – poolie Jun 04 '13 at 09:06