tags:

views:

102

answers:

4

Hi,
when I wrote this ,compile and run:

int x;   
scanf ("%d", &x);  
while (x!=4) {  
    scanf ("%d", &x);  
}

and when inserting char or double number less than 4 it enter an infinite loop.
when inserting double greater than 4 it terminates.
Any explanation?

A: 

The %d you passed to the scanf tells it to parse an int. When you enter a double, it can not parse neither store the parsed data at the x variable because double uses 64 bits on 32 bits machines and int just 32 bits, raising a Seg. Fault or random collateral effects.

Vinícius Gobbo A. de Oliveira
-1 scanf reads **text**. if you enter a decimal point (or an exponent, or any *error* thing) scanf stops because of the inappropriate character.
pmg
+6  A: 

You don't check if the scanf actually succeeded, therefore you will get stuck on error. With each loop, the scanf will try to read and fail.

scanf returns the number of successfully read items therefore modify the loop to something like this while (x!=4) { if (scanf("%d",&x) != 1) break; }

Let_Me_Be
+1 for the correct way to use `scanf`
pmg
vote up for the correct use of scanf()
Aboelnour
+4  A: 

When scanf stops scanning at a specific position in the input stream, it will never advance the stream, so the next scanf will try again the same error ... and again ... and again

input: 42 23 foo ...
scanf: ^
x      42
scanf:   ^
x      23
scanf:      ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable
scanf:       ^
x      unusable
pmg
and what about if i entered a double number less than 4 it enter an infinite loop.and when inserting double greater than 4 it terminates.
Aboelnour
I can't explain that behaviour. The loop stops when x is not 4. But using x after a failed scanf is Undefined Behaviour: anything can happen.
pmg
@Aboelnour If you enter `4.29419541951` it will read the `4` as an integer and exit. If you enter `3.29419541951` it will read the `3` and then fail on the next loop because the first character is `.`. You need to check the return value. Check the code in my answer.
Let_Me_Be
vote up for the first one who get the reason of that issue
Aboelnour
+3  A: 

From the C language standard (n1256):

7.19.6.2 The fscanf function
...
4 The fscanf function executes each directive of the format in turn. If a directive fails, as detailed below, the function returns. Failures are described as input failures (due to the occurrence of an encoding error or the unavailability of input characters), or matching failures (due to inappropriate input).
...
7 A directive that is a conversion specification defines a set of matching input sequences, as described below for each specifier. A conversion specification is executed in the following steps:

8 Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a [, c, or n specifier.250)

9 An input item is read from the stream, unless the specification includes an n specifier. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence.251) The first character, if any, after the input item remains unread. If the length of the input item is zero, the execution of the directive fails; this condition is a matching failure unless end-of-file, an encoding error, or a read error prevented input from the stream, in which case it is an input failure.

10 Except in the case of a % specifier, the input item (or, in the case of a %n directive, the count of input characters) is converted to a type appropriate to the conversion specifier. If the input item is not a matching sequence, the execution of the directive fails: this condition is a matching failure. Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

Emphasis added in paragraph 10. The %d conversion specifier expects the input text to be formatted as a decimal integer. If it isn't, the conversion fails and the character that caused the conversion to fail is left in the input stream. Further calls to scanf() with the %d conversion specifier will choke on the same character.

scanf() returns the number of successful assignments; you need to check this result to see if the conversion succeeded, like so:

int x = 0;
while (x != 4)
{
  int result = scanf("%d", &x);
  if (result != 1)
  {
    printf("Last call to scanf() failed; exiting\n");
    break;
  }
}

Unfortunately, you still have the bad input stuck in the input stream. There are a number of strategies for dealing with this. You could remove the offending character with getchar and try again:

while (x != 4)
{
  int tmp;
  if (scanf("%d", &tmp) == 0)
    getchar();
  else
    x = tmp;
}

Or you could try to read up to the next newline, assuming that all remaining input is b0rked:

while (x != 4)
{
  int tmp;
  if (scanf("%d", &tmp) == 0)
    while (getchar() != '\n')
      ;
  else
    x = tmp;
}

Or you could try to read the input as text and convert to integer using strtol() (my preferred technique):

char input[SOME_SIZE];
int x = 0;
...
while (x != 4)
{
  if (fgets(input, sizeof input, stdin))
  {
    char *check;
    int tmp = (int) strtol(input, &check, 10);
    if (!isspace(*check) && *check != 0)
    {
      printf("%s is not a valid integer: try again\n", input);
    }
    else
    {
      x = tmp;
    }
  }
  else
  {
    printf("Read error on standard input\n");
    break;
  }
}

It's more work, but it allows you to catch bad input before it gets assigned to x.

John Bode
vote up for the reference and for getchar() solution
Aboelnour
Just a small remark. Ignoring part of the input is actually a very bad practice. You should report error on the first sign of trouble and not continue reading.
Let_Me_Be