Оператор выбора switch
Часто возникают ситуации, когда некоторая переменная может принимать несколько возможных значений-вариантов, и для каждого варианта требуется выполнить какие-то свои действия. Например, пользователю может быть предложено меню, когда нажатие различных клавиш инициирует соответствующие команды. Управляющая конструкция, реализующая такую логику, может использовать “последовательно вложенные” операторы if...else if...:
int key;
printf("\nSelect command (F, M or Q): ");
// Вывести подсказку. key = getch();
// Прочитать символ. key = toupper(key);
// Преобразовать в верхний регистр. if (key == 'F')
// Определение команды...
printf("\n\"F\" selected - means File.\n");
else if (key == 'M')
printf("\n\"M\" selected - means Message.\n");
else if (key == 'Q')
printf("\n\"Q\" selected - means Quit.\n");
else
printf("\nlnvalid key!");

Здесь мы применили функцию преобразования символа в верхний регистр toupper (), чтобы можно было вводить букву команды в обоих регистрах (например, F или f).
Условия операторов if содержат проверку кода нажатой клавиши на равенство одному из допустимых символов. Если код клавиши не соответствует никакой команде, выводится сообщение об ошибке.
Для подобных случаев в С существует специальная конструкция выбора switch. Выглядит она так:
switch (выражение)
{
case константное_выражение: группа_операторов case константное_выражение: группа_операторов
[default: группа операторов] }
Сначала производится оценка выражения в операторе switch; полученное значение последовательно сравнивается с каждым из константных_выражений, и при совпадении значений управление передается на соответствующую группу_операторов. Если значение выражения не подходит ни под один из вариантов, управление передается на группу операторов с меткой default или на следующий после блока switch оператор, если группа default отсутствует.
Под группой _операторов подразумевается просто один или несколько произвольных операторов. Группа здесь вовсе не обязана быть блоком, т. е. заключать ее в операторные скобки не требуется.
И еще одна особенность, о которой следует помнить при написании структур switch. Если найдена метка case, совпадающая со значением проверяемого выражения, то выполняется группа_операторов данного case. Однако дело на этом не заканчивается, поскольку, если не принять никаких дополнительных мер, управление “провалится” ниже, на следующую по порядку метку case и т. д., и в результате будут выполнены все операторы до самого конца блока switch. Если это нежелательно (как чаще всего и бывает), в конце группы_операторов case нужно поставить оператор break. Он прерывает выполнение блока switch и передает управление оператору, непосредственно следующему за блоком.
Для иллюстрации мы перепишем предыдущий пример “меню”, оформив его в виде законченной программы, и продемонстрируем попутно еще один управляющий оператор С.
Листинг 3.3. Демонстрация структуры switch
/*
** Switch.с: Оператор выбора.
*/
#pragma hdrstop
#include <stdio.h>
#include <conio.h>
#pragma argsused
int rriain(int argc, char* argv[])
{
int key;
loop:
printf("\nEnter command (F, M or Q): ");
key = getche(); // Прочитать клавишу.
switch (key) ( // Определение команды... case 'f':
case 'F":
printf("\n\"File\" command selected.\n");
break;
case 'm':
case 'M':
printf ("\n\"Mess.age\" command selected.\n");
break;
case 'q':
case 'Q':
printf("\n\"Quit\" command selected.\n");
printf("\nPress a key to Exit...");
getch() ;
return 0; // Возврат в Windows. default:
printf("\nlnvalid command!\n") ;
}
goto loop; // Следующая команда.
}

Рис. 3.3 Программа Switch
Мы организовали в программе простейший “бесконечный” цикл, который все время просит пользователя ввести команду — до тех пор, пока не будет нажата клавиша “Q”. В этом случае происходит возврат в операционную систему.

Чтение команды производится функцией getche(). Она, как и getch (), возвращает код нажатой клавиши, однако в отличие от getch () отображает введенный символ на экране.
Для реализации цикла мы использовали архаический оператор goto, исполнение которого приводит к передаче управления на метку, указанную в операторе. В примере метка с именем loop стоит в самом начале программы. Таким образом, дойдя до конца функции main (), управление возвращается к ее началу и все повторяется снова.

Как я сказал, оператор goto весьма архаичен, и он, конечно, никак не укладывается в концепцию структурированного потока управления. Единственным случаем, когда его использование в программе на С может быть оправдано, является обработка некоторых аварийных ситуаций. Если имеется очень сложная система вложенных друг в друга управляющих структур и ошибка происходит где-то глубоко внутри, то проще и надежнее всего передать управление на процедуру обработки ошибок с помощью goto.