scanf не работает, почему?


когда я написал это, скомпилируйте и запустите:

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

и при вставке char или двойного числа меньше 4 он входит в бесконечный цикл.
при вставке двойного числа больше 4 он завершается.
Любое объяснение?


person Aboelnour    schedule 25.10.2010    source источник


Ответы (4)


Из стандарта языка C (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.

Выделение добавлено в параграф 10. Спецификатор преобразования %d предполагает, что входной текст будет отформатирован как десятичное целое число. Если это не так, преобразование завершается ошибкой, и символ, вызвавший сбой преобразования, остается во входном потоке. Дальнейшие вызовы scanf() со спецификатором преобразования %d будут подавляться тем же символом.

scanf() возвращает количество успешных назначений; вам нужно проверить этот результат, чтобы увидеть, удалось ли преобразование, например:

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

К сожалению, у вас все еще есть неверный ввод, застрявший во входном потоке. Есть несколько стратегий борьбы с этим. Вы можете удалить оскорбительный символ с помощью getchar и повторить попытку:

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

Или вы можете попытаться прочитать до следующей новой строки, предполагая, что весь оставшийся ввод b0rked:

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

Или вы можете попробовать прочитать ввод как текст и преобразовать в целое число, используя strtol() (мой предпочтительный метод):

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;
  }
}

Это требует больше работы, но позволяет выявлять неверные входные данные до того, как они будут назначены x.

person John Bode    schedule 25.10.2010
comment
Просто небольшое замечание. Игнорирование части ввода на самом деле очень плохая практика. Вы должны сообщить об ошибке при первых признаках проблемы и не продолжать чтение. - person Šimon Tóth; 25.10.2010

Вы не проверяете, действительно ли scanf удалось, поэтому вы застрянете на ошибке. В каждом цикле scanf будет пытаться читать и терпит неудачу.

scanf возвращает количество успешно прочитанных элементов, поэтому измените цикл на что-то вроде этого while (x!=4) { if (scanf("%d",&x) != 1) break; }

person Šimon Tóth    schedule 25.10.2010

Когда scanf останавливает сканирование в определенной позиции во входном потоке, он никогда не будет продвигать поток вперед, поэтому следующий scanf попытается повторить ту же ошибку... и снова... и снова

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
person pmg    schedule 25.10.2010
comment
а как насчет того, если я ввел двойное число меньше 4, он войдет в бесконечный цикл, а при вставке двойного числа больше 4 он завершится. - person Aboelnour; 25.10.2010
comment
Я не могу объяснить такое поведение. Цикл останавливается, когда x не равен 4. Но использование x после неудачного сканирования f является неопределенностью поведения: все может случиться. - person pmg; 25.10.2010
comment
@Aboelnour Если вы введете 4.29419541951, он прочитает 4 как целое число и выйдет. Если вы введете 3.29419541951, он прочитает 3, а затем потерпит неудачу в следующем цикле, потому что первый символ - .. Вам нужно проверить возвращаемое значение. Проверьте код в моем ответе. - person Šimon Tóth; 25.10.2010

%d, который вы передали в scanf, говорит ему проанализировать целое число. Когда вы вводите double, он не может ни проанализировать, ни сохранить проанализированные данные в переменной x, потому что double использует 64 бита на 32-битных машинах, а int только 32 бита, поднимая Seg. Неисправность или случайные побочные эффекты.

person Vinícius Gobbo A. de Oliveira    schedule 25.10.2010
comment
-1 scanf читает текст. если вы введете десятичную точку (или показатель степени, или любую другую ошибку), scanf остановится из-за неподходящего символа. - person pmg; 25.10.2010