Как исправить ошибки буфера в то время как?

Когда вы запускаете этот код, он как-то работает, а также количество символов, введенных scanf после первой операции. В чем проблема?

Я заполнил пробел перед %c, потому что подумал, что это может быть проблема с буфером, но ошибка все еще возникает.

void EX18() {
    float x, y;
    char op;
    int i = 0;
    while (1) {
        /*
        printf("\n******************** \n");
        printf("A ---- Add \n");
        printf("S ---- Subtract \n");
        printf("M ---- Multiply \n");
        printf("D ---- Divide \n");
        printf("Q ---- Quit \n");
        printf("******************** \n");
        */
        i++;
        printf("\n%dth iteration \n",i);
        printf("Select an operation: ");
        scanf(" %c", &op);

        if (op == 'Q') break;
        else {
            switch (op) {
            case 'A':
                printf("Enter two numbers separated by a space: ");
                scanf("%f %f", &x, &y);
                printf("%.2f \n", x + y);
                break;
            case 'S':
                printf("Enter two numbers separated by a space: ");
                scanf("%f %f", &x, &y);

                printf("%.2f \n", x - y);
                break;
            case 'M':
                printf("Enter two numbers separated by a space: ");
                scanf("%f %f", &x, &y);

                printf("%.2f \n", x * y);
                break;
            case 'D':
                printf("Enter two numbers separated by a space: ");
                scanf("%f %f", &x, &y);

                printf("%.2f \n", x / y);
                break;
            default:
                printf("\nUnknown command. Please try again.\n");
            }
        }
    }
}

int main()
{
    EX18();
    //TEST5();
    return 0;
}

Экран результатов:

1-я итерация Выберите операцию: 00

Неизвестная команда. Пожалуйста, попробуйте еще раз.

2-я итерация Выберите операцию: Неизвестная команда. Пожалуйста, попробуйте еще раз.

3-я итерация Выберите операцию: 000

Неизвестная команда. Пожалуйста, попробуйте еще раз.

4-я итерация Выберите операцию: Неизвестная команда. Пожалуйста, попробуйте еще раз.

5-я итерация Выберите операцию: Неизвестная команда. Пожалуйста, попробуйте еще раз.

6-я итерация Выберите операцию:

Несвязанный: запрос чисел и их чтение выполняются одинаково во всех случаях. Почему бы не переместить его за пределы switch? Кроме того, в наши дни это почти бесполезно float. Если у вас нет особых требований, вместо этого используйте double для чисел с плавающей запятой.

Some programmer dude 19.04.2023 14:28

Что касается вашей ошибки, в чем ваша ошибка? Пожалуйста, отредактируйте свой вопрос, чтобы сообщить нам о том, что вы вводите, а также о фактических и ожидаемых результатах.

Some programmer dude 19.04.2023 14:28

Мне жаль. Случайно забыл картинку экрана результатов.

Gongback Kim 19.04.2023 14:29

Пожалуйста, не размещайте изображения текста. Скопируйте и вставьте весь текст как фактический текст в свой вопрос.

Some programmer dude 19.04.2023 14:30

Без проверки возвращаемого значения ваших различных scanf() вы не знаете, действительно ли x и y инициализируются. Никогда не используйте scanf(), не проверив возвращаемое значение.

DevSolar 19.04.2023 14:33

Совет от @DevSolar хороший. Еще лучше было бы забыть, что scanf вообще существует. Используйте fgets, чтобы читать целые строки. Затем вы можете использовать sscanf для анализа ввода. Но снова послушайте, что говорит @DevSolar, и проверьте возвращаемое значение sscanf.

Some programmer dude 19.04.2023 14:36

Не размещайте фотографии текста. Вывод - текст. Скопируйте вывод и опубликуйте его как правильно отформатированный текст.

Jabberwocky 19.04.2023 14:37

Ваша проблема в том, что вы просите пользователя ввести «A», «S», «M» или «D», чтобы указать операцию, но затем вы вводите такие вещи, как «OO» и «OOO». Зачем ты это делаешь? Если вводить одиночные символы, как и просили, программа работает нормально, по крайней мере у меня.

Steve Summit 19.04.2023 14:41

Выполнение пользовательского ввода с помощью scanf имеет определенные ограничения. Одним из ограничений является сложность или невозможность восстановления после неправильного ввода. Итак, когда вы тестируете программу, использующую scanf, вы всегда должны вводить правильные, а не неправильные входные данные. Если вы хотите написать программу, которая может изящно справляться с неправильным вводом (например, «ООО», когда ожидался один символ), вам будет гораздо лучше написать ее с помощью чего-то другого, кроме scanf.

Steve Summit 19.04.2023 14:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что вы читаете «команду» посимвольно. Если вы введете два недопустимых символа, первый вызов scanf(" %c", &op) прочитает первый, но второй недопустимый ввод все еще находится в буфере, чтобы быть прочитанным следующим вызовом.

Как я советовал в комментарии, используйте fgets, чтобы читать целые строки, а затем, например. sscanf для анализа ввода:

char buffer[256];  // No need to skimp on space

if (!fgets(buffer, sizeof buffer, stdin))
{
    // Failed to read input
    // TODO: Handle this case
}

if (sscanf(" %c", &op) != 1)
{
    // Invalid input
    // TODO: Handle this case
}

Это больше кода, но он будет более надежным и безопасным, чем ваш текущий код.

Сделайте что-то подобное для числового ввода.

Я также предлагаю вам поместить их в отдельные функции, что упрощает чтение кода, его тестирование и отладку при необходимости.


Обратите внимание, что хотя код, описанный выше, сделает вашу обработку ввода лучше и безопаснее, он не идеален.

Назойливый пользователь может ввести строку длиннее 255 символов, и в этом случае мы в основном возвращаемся к тому, с чего начали, где в буферах остается избыточный ввод.

Чтобы быть действительно безопасным от этого, вы должны проверить, является ли последний символ во вводе из fgets новой строкой, и если нет, то сообщите пользователю о недопустимом вводе и используйте fgets (возможно, даже просто fgetc) в цикле, пока вы не прочитаете новая строка.

Спасибо за ваш ответ!! Мое любопытство разрешилось.

Gongback Kim 19.04.2023 14:52
Ответ принят как подходящий

Ключевой частью вашей программы являются две строки

printf("Select an operation: ");
scanf(" %c", &op);

Предположим, вы набираете A. scanf читает один символ, присваивает его op, последующая часть вашей программы распознает A для добавления и делает свое дело.

Предположим, вы набираете O. scanf читает один символ, присваивает его op, но более поздняя часть вашей программы не распознает символ и поэтому печатает «Неизвестная команда».

Предположим, вы набираете XYZ. scanf читает первый символ X и присваивает его op. Но, опять же, более поздняя часть вашей программы не распознает символ и поэтому печатает «Неизвестная команда». Но на этот раз, когда программа возвращается к началу, эти два непрочитанных символа YZ все еще находятся во входном потоке. Таким образом, scanf может сразу прочитать следующий символ, Y, и назначить его op. Что более поздняя часть вашей программы не распознает. И затем, когда программа зацикливается в третий раз, она уже получает этот Z во входном потоке для чтения.

Ключевые вещи, которые нужно понять:

  1. scanf не читает строки ввода.
  2. Во входном потоке остается непрочитанный или нераспознанный ввод.

Таким образом, программа, использующая scanf, очень часто запутывается, когда какой-то непрочитанный или нераспознанный ввод, напечатанный в том, что вы считали «предыдущей строкой», на самом деле все еще висит во входном буфере, где он считывается более поздним вызовом. на scanf, казалось бы, сразу, даже не дожидаясь, пока вы что-нибудь наберете.

Хотя scanf достаточно просто использовать для простого ввода, на самом деле это довольно странная функция и, как вы видели, может быть очень запутанной. В частности, если вы думаете, что пользователь вводит строку текста и нажимает клавишу «Ввод», часто лучше, чтобы ваша программа работала так же, читая ввод построчно, используя что-то вроде fgets, как это предлагается в нескольких комментариев. См. этот ответ для получения дополнительной информации о чтении ввода с использованием чего-то другого, кроме scanf.

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

Другие вопросы по теме