Scanf не удается, почему? - PullRequest
       1

Scanf не удается, почему?

2 голосов
/ 25 октября 2010


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

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

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

Ответы [ 4 ]

11 голосов
/ 25 октября 2010

Из языкового стандарта C (n1256) :

7.19.6.2 Функция fscanf
...
4 Функция fscanf выполняетсякаждая директива формата по очереди.В случае сбоя директивы, как описано ниже, функция возвращается.Сбои описываются как сбои ввода (из-за возникновения ошибки кодирования или недоступности вводимых символов) или сбоев сопоставления (из-за неправильного ввода).
...
7 Директива, которая является спецификацией преобразованияопределяет набор соответствующих входных последовательностей, как описано ниже для каждого спецификатора.Спецификация преобразования выполняется в следующие шаги:

8 Входные пробельные символы (как определено функцией isspace) пропускаются, если в спецификации не указано [, cСпецификатор или n . 250)

9 Элемент ввода считывается из потока, если в спецификации не указано n спецификатор.Элемент ввода определяется как самая длинная последовательность входных символов, которая не превышает заданную ширину поля и которая является или является префиксом соответствующей входной последовательности. 251) Первый символ, если имеется,после ввода элемент остается непрочитанным.Если длина входного элемента равна нулю, выполнение директивы заканчивается неудачей;это условие является ошибкой сопоставления, если только конец файла, ошибка кодирования или ошибка чтения не предотвратили ввод из потока, и в этом случае это сбой ввода.

10 За исключением случая со спецификатором % , элемент ввода (или, в случае директивы % n , количество вводимых символов)преобразован в тип, соответствующий спецификатору преобразования. Если элемент ввода не совпадает с последовательностью, выполнение директивы завершается неудачно: это условие является ошибкой совпадения. Если подавление присваивания не было указано *, результат преобразования помещается в указанный объектпервым аргументом после аргумента формата, который еще не получил результат преобразования.Если этот объект не имеет подходящего типа или если результат преобразования не может быть представлен в объекте, поведение не определено.

Выделение добавлено в параграф 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.

6 голосов
/ 25 октября 2010

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

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

5 голосов
/ 25 октября 2010

Когда 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
0 голосов
/ 25 октября 2010

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

...