Задачи
Давайте рассмотрим задачи, которые необходимо решать в программе с реализованным меню. Программа должна получать ответ от пользователя и на его основе вы-
Символьный ввод-вывод и проверка достоверности ввода 317
бирать дальнейший ход действий. Кроме того, программа должна обеспечить способ возврата меню для последующего выбора вариантов. Оператор switch в С является естественным механизмом для выбора действий, т.к. каждый выбор пользователя соответствует конкретной метке case. Оператор while можно использовать для предоставления повторяющегося доступа к меню. С помощью псевдокода процесс можно описать следующим образом:
получить вариант
пока не выбран вариант 'в'
переключиться на нужный вариант и выполнить его получить следующий вариант
На пути к более гладкому выполнению
Цели гладкости программы (гладкость при обработке как корректного, так и некорректного ввода) вступает в игру, когда вы решаете, каким образом реализовать указанный план. Например, одно из действий, которое вы можете предпринять — это исключить в части “получить вариант” неправильные ответы, чтобы оператору switch передавались только корректные варианты. Это предполагает представление процесса ввода в виде функции, которая возвращает только корректные ответы. В сочетании с циклом while и оператором switch получается следующая структура программы:

Функция get choicet) определена так, что она может возвращать только значения 'с', 'з', ' п' и 'в'. Вы используете ее во многом также, как getchar)), получая значение и сравнивая его со значением завершения (в данном случае 'в'). Выбираемые варианты в меню специально упрощены, чтобы можно было сосредоточиться на структуре программы; вскоре мы рассмотрим функцию count(). Конструкция default удобна для отладки. Если функции get choice() не удается ограничить свое возвращаемое значение, как было запланировано, то случай default позволяет узнать, что происходит что-то подозрительное.
318 Глава 8
Функция gat_choice()
Ниже приведен псевдокод одной из возможных структур этой функции:

Вот простая, но довольно неуклюжая реализация:
Проблема заключается в том, что при буферизированном вводе каждый символ новой строки, сгенерированный нажатием клавиши , трактуется как ошибочный отклик. Чтобы сделать интерфейс программы более гладким, эта функция должна пропускать символы новой строки.
Существует несколько способов сделать это. Один из них предусматривает замену getchar() новой функцией по имени get_f irst(), которая читает первый символ в с троке и отбрасывает все остальные. Преимущество такого метода состоит в том, что он трактует введенную строку, например, спа, как просто символ с, но не рассматривает ее как правильный вариант с, за которым следует еще один правильный вариант в виде буквы n, означающей подсчет. С учетом всего этого, функцию ввода можно переписать следующим образом:

Символьный ввод-вывод и проверка достоверности ввода 319

Смешивание символьного и числового ввода
Создание меню является еще одной иллюстрацией того, что смешивание символьного и числового ввода может привести к проблемам. Предположим, например, что функция count() (выбор n) имеет такой вид:
void count(void)
{
int n,i;
printf("До какого предела вести подсчет? Введите целое число:\n"); scanf("%d", &n); for (i = 1; i <= n; i++) printf("%d\n", i);
}
Если вы ответите вводом 3, функция scanf() прочитает 3 и оставит символ новой строки в качестве следующего символа во входной очереди. Следующий вызов get choice() привел бы к тому, что функция get_f irst() возвратила бы этот символ новой строки, что привело бы к нежелательному поведению.
Один из способов устранить эту проблему — переписать функцию get first() так, чтобы она возвращала следующий непробельный символ, а не просто любой следующий символ. Мы оставляем эту задачу для самостоятельного выполнения. Второй подход — заставить саму функцию count() следить за порядком и удалять символ новой строки. Именно этот подход применяется в следующем примере:
void count(void)
{
int n,i;
printf("До какого предела вести подсчет? Введите целое число:\n"); n = get_int(); for (i = 1; i <= n; i++) printf("%d\n", i); while ( getchar() != '\n') continue;
Читать дальше