Когда вы запускаете этот код, он как-то работает, а также количество символов, введенных 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-я итерация Выберите операцию:
Что касается вашей ошибки, в чем ваша ошибка? Пожалуйста, отредактируйте свой вопрос, чтобы сообщить нам о том, что вы вводите, а также о фактических и ожидаемых результатах.
Мне жаль. Случайно забыл картинку экрана результатов.
Пожалуйста, не размещайте изображения текста. Скопируйте и вставьте весь текст как фактический текст в свой вопрос.
Без проверки возвращаемого значения ваших различных scanf()
вы не знаете, действительно ли x
и y
инициализируются. Никогда не используйте scanf()
, не проверив возвращаемое значение.
Совет от @DevSolar хороший. Еще лучше было бы забыть, что scanf
вообще существует. Используйте fgets
, чтобы читать целые строки. Затем вы можете использовать sscanf
для анализа ввода. Но снова послушайте, что говорит @DevSolar, и проверьте возвращаемое значение sscanf
.
Не размещайте фотографии текста. Вывод - текст. Скопируйте вывод и опубликуйте его как правильно отформатированный текст.
Ваша проблема в том, что вы просите пользователя ввести «A», «S», «M» или «D», чтобы указать операцию, но затем вы вводите такие вещи, как «OO» и «OOO». Зачем ты это делаешь? Если вводить одиночные символы, как и просили, программа работает нормально, по крайней мере у меня.
Выполнение пользовательского ввода с помощью scanf
имеет определенные ограничения. Одним из ограничений является сложность или невозможность восстановления после неправильного ввода. Итак, когда вы тестируете программу, использующую scanf
, вы всегда должны вводить правильные, а не неправильные входные данные. Если вы хотите написать программу, которая может изящно справляться с неправильным вводом (например, «ООО», когда ожидался один символ), вам будет гораздо лучше написать ее с помощью чего-то другого, кроме scanf.
Проблема в том, что вы читаете «команду» посимвольно. Если вы введете два недопустимых символа, первый вызов 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
) в цикле, пока вы не прочитаете новая строка.
Спасибо за ваш ответ!! Мое любопытство разрешилось.
Ключевой частью вашей программы являются две строки
printf("Select an operation: ");
scanf(" %c", &op);
Предположим, вы набираете A
. scanf
читает один символ, присваивает его op
, последующая часть вашей программы распознает A
для добавления и делает свое дело.
Предположим, вы набираете O
. scanf
читает один символ, присваивает его op
, но более поздняя часть вашей программы не распознает символ и поэтому печатает «Неизвестная команда».
Предположим, вы набираете XYZ
. scanf
читает первый символ X
и присваивает его op
. Но, опять же, более поздняя часть вашей программы не распознает символ и поэтому печатает «Неизвестная команда». Но на этот раз, когда программа возвращается к началу, эти два непрочитанных символа YZ
все еще находятся во входном потоке. Таким образом, scanf
может сразу прочитать следующий символ, Y
, и назначить его op
. Что более поздняя часть вашей программы не распознает. И затем, когда программа зацикливается в третий раз, она уже получает этот Z
во входном потоке для чтения.
Ключевые вещи, которые нужно понять:
scanf
не читает строки ввода.Таким образом, программа, использующая scanf
, очень часто запутывается, когда какой-то непрочитанный или нераспознанный ввод, напечатанный в том, что вы считали «предыдущей строкой», на самом деле все еще висит во входном буфере, где он считывается более поздним вызовом. на scanf
, казалось бы, сразу, даже не дожидаясь, пока вы что-нибудь наберете.
Хотя scanf
достаточно просто использовать для простого ввода, на самом деле это довольно странная функция и, как вы видели, может быть очень запутанной. В частности, если вы думаете, что пользователь вводит строку текста и нажимает клавишу «Ввод», часто лучше, чтобы ваша программа работала так же, читая ввод построчно, используя что-то вроде fgets
, как это предлагается в нескольких комментариев. См. этот ответ для получения дополнительной информации о чтении ввода с использованием чего-то другого, кроме scanf
.
См. также этот предыдущий вопрос для дальнейшего обсуждения того, как scanf
на самом деле работает.
Несвязанный: запрос чисел и их чтение выполняются одинаково во всех случаях. Почему бы не переместить его за пределы
switch
? Кроме того, в наши дни это почти бесполезноfloat
. Если у вас нет особых требований, вместо этого используйтеdouble
для чисел с плавающей запятой.