бесконечный цикл (scanf), не понимаю почему

199
15 января 2019, 15:10
#include <stdio.h>
int main(void)
{
    int num;
    do
    {
        scanf("%d", &num);
        if (num >= 1 && num <= 2000)
        {
            // Ниже тело цикла --------------------------
            if (num <= 4)
            {
                printf("few\n");
            }
            else if (num <= 9)
            {
                printf("several\n");
            }
            else if (num <= 19)
            {
                printf("pack\n");
            }
            else if (num <= 49)
            {
                printf("lots\n");
            }
            else if (num <= 99)
            {
                printf("horde\n");
            }
            else if (num <= 249)
            {
                printf("throng\n");
            }
            else if (num <= 499)
            {
                printf("swarm\n");
            }
            else if (num <= 999)
            {
                printf("zounds\n");
            }
            else
            {
                printf("legion\n");
            }
        } // закрывает "if (num >= 1 && num <= 2000)"
    } // закрывает "DO"
    while ((num < 1) || (num > 2000));
}

Как-то по-уродски код вставился...

Задача: На вход программа просит число. Если число в диапазоне от 1 до 2000, то следует выполнить тело цикла. И повторять это до тех пор, пока число меньше 1 или больше 2000.

В целом все работает. Проблемы возникают тогда, когда я ввожу не число. К примеру "q". Тогда программа будет бесконечно просить меня ввести что-то, и ей уже все ровно, что я ввожу нужные значения. Не понимаю, почему не работает. Мой взгляд на вещи:

  1. Обьявляем переменную num
  2. Запускаем цикл DO
  3. Программа запрашивает данные (scanf)
  4. Допустим я ввожу "q"
  5. Т.к num - переменная типа int, я думаю до цикла if даже не дойдет
  6. Проверка на завершения цикла while, но т.к условию num явно не удовлетворяет, то цикл идет заного
  7. у меня опять запрашивают данные (scanf) и я ввожу "21"
  8. На этом этапе значение num должно пройти в цикл if и цикл while должен завершиться т.к соблюдаются условия по выходу из цикла
  9. вместо этого я получают цикл из бесконечных scanf....

и еще вопрос: почему я не получаю ошибку, когда пытаюсь ввести что-то, отличное от типа int? разве "&num" - это ни есть присвоение? всё уже должно было взорваться, когда я попытался впихнуть туда "q".

Answer 1

Нужно проверять, что возвращает scanf. Сделайте так:

    if (scanf("%d", &num) != 1)
    {
        while(fgetc(stdin) != '\n');
        continue;
    }

Т.е. пока не считано нормальное число - игнорировать введеннуюю строку и начать цикл заново...

У вас, встретив не цифру, scanf оставляет ее в буфере, и при повторении цикла опять встречает ее же...

Answer 2

Вам следует проверять результат работы функции scanf, и если было введено не число, то пропускать введенные символы. Это можно сделать следующим образом

int num;
do
{
    int result = scanf( "%d", &num );
    if (result == EOF) break;
    if (result != 1)
    {
        scanf("%*[^\n]");
        num == 0;
    }
    if (num >= 1 && num <= 2000)
    {
        // Ниже тело цикла --------------------------
        if (num <= 4)
        {
            printf("few\n");
        }
        else if (num <= 9)
        {
            printf("several\n");
        }
        else if (num <= 19)
        {
            printf("pack\n");
        }
        else if (num <= 49)
        {
            printf("lots\n");
        }
        else if (num <= 99)
        {
            printf("horde\n");
        }
        else if (num <= 249)
        {
            printf("throng\n");
        }
        else if (num <= 499)
        {
            printf("swarm\n");
        }
        else if (num <= 999)
        {
            printf("zounds\n");
        }
        else
        {
            printf("legion\n");
        }
    } // закрывает "if (num >= 1 && num <= 2000)"
} while ((num < 1) || (num > 2000)); // закрывает "DO"

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

    if (result != 1)
    {
        scanf("%*[^\n]");
        num == 0;
    }

и вы сможете повторить итерацию цикла.

Answer 3
scanf("%d", &num);

Если мы уверены, что никаких EOF пользователь не введёт, то нам надо дождаться ввода числа (scanf вернёт 1) выкидывая нечисловые значения (ведь мусор, лежащий в потоке, сколько не пытайся его читать, числом-то не становится):

while (scanf("%d", &num) != 1)
  scanf("%*s");

Если мы хотим ещё и EOF проверять (который -1), то можно немного переписать условие:

while (scanf("%d", &num) == 0)
  scanf("%*s");

или

while (!scanf("%d", &num))
  scanf("%*s");

Если хочется порпускать всю строку целиком, то можно использовать

while (!scanf("%d", &num))
  scanf("%*[^\r\n]");

Как-то так.

Answer 4

Да эта беда всегда была в scanf и им поэтому никто не пользуется, он выпихивает назад что ему не нравится по ungetc, короче надо строчку через gets ввести, а потом её в sscanf

READ ALSO
Как вывести большие(очень большие) числа?

Как вывести большие(очень большие) числа?

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

184
Вывод массива в C++ (При сравнении массивов)

Вывод массива в C++ (При сравнении массивов)

Задается 2 массива a и b, нужно вывести массив имеющий наименьшее кол-во положительных элементовПример: Массив a { 1, 2,3}, массив b { -4, -5, 6} программа...

158
C++ Windows API цикл обработки сообщений

C++ Windows API цикл обработки сообщений

Есть Windows API окно обернутое в класСоздается в Windows API main методе например так:

158
Создание таблиц в PostgreSql из сгенерированных POJO классов из схемы AVRO

Создание таблиц в PostgreSql из сгенерированных POJO классов из схемы AVRO

Всем привет! Может кто сталкивался или знает куда копать?

199