Давыдов В.Г. - Программирование и основы алгоритмизации - 2003
.pdfв заключение отметим, что при ветвлении на два направления достаточно использовать один оператор if. При ветвлении на три и более направлений можно использовать вложенные операторы if В последнем случае возможна и другая альтернатива, которая рас сматривается далее.
4.7. Оператор switch
Вернемся к рассмотренному выше в конце подразд. 4.6 приме ру. Этот пример трудно читать и еще труднее сопровождать. В подобной ситуации, когда требуется передавать управление одному из нескольких операторов в зависимости от значения выражения, можно использовать оператор switch (проверяемое выражение при этом не может иметь вещественный тип). Приведенный выше фрагмент при использовании оператора switch приобретает следующий более наглядный и удобный вид:
char |
|
|
|
ch; |
|
|
switch |
( |
ch ) |
|
|
|
|
{ |
|
'a': |
|
|
|
|
case |
= |
1; |
break; |
|
||
case |
1ndex |
|
||||
'Ь': |
= |
2; |
break/ |
|
||
|
|
index |
|
|||
case |
'у': |
|
|
break; |
|
|
case |
Index |
= |
25; |
|
||
'z ': |
=^ 26; |
break; |
|
|||
|
|
index |
|
|||
default: |
|
|
|
|
||
} |
|
index |
= |
0; |
|
|
|
|
|
|
|
|
|
Синтаксис |
оператора switch |
(переключатель) поясняется диа |
||||
граммой, приведенной на рис. 33. |
|
|||||
Выполнение оператора switch |
начинается с вычисления заклю |
|||||
ченного в скобки |
"выражения", которое долэюно давать результат |
|||||
целого |
или символьного |
типа. Затем просматриваются друг за дру |
гом префиксы case (случай), вычисляются указанные после служеб ного слова case "константныевыражения" и полученные значения сравниваются со значением выражения, указанного после служебно го слова switch. Если эти результаты совпали, то управление переда ется оператору, следующему за соответствующим служебным сло вом case. Если ни одного совпадения не произошло и при этом ука зано необязательное служебное слово default (по умолчанию), то
100
управление передается оператору, следующему за default. Если ни одного совпадения не произошло, а служебное слово default отсут ствует, то управление передается оператору, непосредственно сле дующему за последней фигурной скобкой оператора switch.
OnepaTop_switch |
|
|
|
|
|
|
|
|
|
( switch V ^ T |
( |
V H Выражение |
V->( |
) |
V w |
OnepaTop_case |
|||
Оператор case |
|
|
|
|
|
|
|
|
|
case |
'N |
\ |
I Константное |
I |
I |
^ / ' ^ T ^ |
[ |
Оператор V-ffi } |
|
|
J |
_выражение |
| |
V |
' У |
I fI |
|
Ц^ default y
Рис. 33. Синтаксическая диаграмма переключателя
После передачи управления в блок операторов case исполне ние указанных в нем операторов производится до конца блока (от одной альтернативы к другой), если только последовательность вы полнения операторов не будет изменена операторами break или goto. Действие оператора break сводится к передаче управления оператору, следующему за последней закрывающей фигурной скоб кой switch (рис. 34). Следует также отметить, что порядок следова ния case в блоке безразличен.
Подведем итоги всему сказанному. Для программирования ветвлений на три или более направлений имеются две альтернативы - использование вложенных операторов //или использование опера тора switch. Из приведенных примеров видно, что использование оператора switch является более наглядным и простым. Однако этим оператором нельзя воспользоваться, если проверяемое выражение имеет вещественный тип или если проверяется комбинация не скольких условий. В этом случае приходится использовать вложен ные операторы if
4,8. Оператор while
Этот оператор уже рассматривался нами. Он является операто ром цикла с предусловием (рис. 35):
101
switch( выражение )
{
case конст._выр,_1: Операторы break; —
case конст._выр,_2: Операторы break;
//Выражение не может быть
//вещественного типа
Рис. 34. Переключатель
Оператор while |
|
-Н(^ while ")-КГО~Л Выражение |
Оператор |
Рис. 35. Синтаксическая диаграмма оператора while
Работа оператора while заключается в следующем.
1. Вычисляется значение "выражения" и, если его тип отличен от целого, то полученное значение приводится к целому типу.
2.Если значение "выражения" отлично от нуля ("истина"), то выполняется "оператор" и осуществляется переход к п. 1.
3.Если значение "выражения" равно нулю ("ложь"), то выпол нение цикла завершается и управление передается оператору, сле дующему за оператором while.
Таким образом, оператор while эквивалентен следующей по следовательности операторов:
cycle: |
±f( выражение ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
опера |
тор |
|
управление |
на |
оператор^ |
|
|
// Оператор передает |
об |
|||||
|
// |
следующий |
за меткой cycle |
(см. |
подробнее |
||
|
// |
операторе |
goto |
ниже) |
|
|
|
|
goto |
cycle; |
|
|
|
|
|
}
"Выражение", используемое в операторе while, называется ус ловием повторения цикла^ В общем случае, конечно же, одним из результатов выполнения "оператора", составляющего тело цикла, должно быть изменение значений переменных, входящих в "выра жение". В противном случае оператор while выполнялся бы беско нечно.
102
Обычно тело цикла представляет собой блок операторов. Это позволяет обеспечить циклическое выполнение группы операторов. Но в частном случае, если "выражение" имеет нулевое значение, те ло цикла не будет выполняться ни разу. Это является отличительной особенностью циклов с предусловием.
Рассмотрим и проанализируем ряд специальных примеров.
/ / пример |
1 |
== 1 |
) ; |
// |
В качестве |
тела цикла |
использован |
while ( fun( |
) |
||||||
|
|
|
|
// |
пустой |
оператор |
|
В этом примере вся работа осуществляется за счет выполнения |
|||||||
условия цикла |
и не |
требуется |
никаких |
других операторов (fun() - |
функция, возвращающая некоторое целое значение; как только она вернет значение, отличающееся от единицы - выполнение цикла за кончится). В подобных случаях щ\л удовлетворения синтаксических правил следует в качестве тела цикла указать точку с запятой, кото рая обозначает пустой оператор.
/ / пример |
2 |
while ( 1 |
) |
{ |
|
bxrea-k |
; |
} |
|
Этот пример демонстрирует преднамеренное создание "беско нечного" цикла. Так как "выражение" в этом цикле всегда имеет значение 1 ("истина"), то цикл может исполняться неограниченное число раз. Одним из способов завершения выполнения такого цикла является использование в его теле оператора break. Как только это произойдет, управление будет передано оператору программы, сле дующему за оператором while. Оператор break подобным же обра зом действует в теле цикла и других циклических операторов, кото рые будут рассмотрены ниже.
Циклические операторы, так же как и условные операторы,
можно вкладывать друг в друга. В подобных |
случаях |
оператор |
break |
||
обеспечивает |
выход только |
из того цикла, |
в теле |
которого |
он на |
ходится. |
|
оператор continue^ который позволяет |
|||
В языке |
предусмотрен |
пропускать оставшуюся после него часть тела цикла и начать новую итерацию, т.е. новое выполнение тела цикла сначала. Таким обра зом, по своему действию операторы break и continue являются опе раторами с ограниченным диапазоном передачи управления. Дейст вие их показано на рис. 36.
103
while( выражение) |
while( выражение ) |
{ |
{ |
break; |
continue; |
} |
} |
Рис. 36. Использование операторов break и continue в цикле while
4.9. Оператор do-while
Оператор do-while, часто называемый оператор do, является циклом с постусловием и имеет синтаксическую диаграмму, пред ставленную на рис. 37 (обратите внимание на наличие в конце опе ратора точки с запятой).
Оператор do-while
- ^ / ddo j - H Оператор V-U while V ^ / ( V H Выражение Н->Г ) \>( \ )—•
Рис. 37. Синтаксическая диаграмма оператора do-while
Работа оператора поясняется следующей эквивалентной по следовательностью операторов:
cycle:
оператор
±£( выражение ) goto cycle;
Из эквивалентного представления следует, в частности, что в отличие от цикла while, тело цикла do-while, независимо от значения "выражения", будет выполнено не менее одного раза. Для изменения хода выполнения операторов, составляющих тело цикла можно вос пользоваться операторами break и continue (рис. 38).
do |
do |
{ |
{ |
Ьгяак" |
continue* |
} |
} |
while( выражение ); |
while( выражение); |
Рис. 38. Операторы break и continue в цикле do-while
104
Остальные особенности цикла while (использование пустого оператора, "бесконечный" цикл, вложенность циклов и т.п.) в рав ной степени относятся и к циклу do-while.
4.10Оператор for
Этот оператор также является циклом с предусловием и имеет синтаксическую диаграмму, представленную на рис. 39. Работа опе ратора for поясняется следующей эквивалентной последовательно стью операторов:
выражение!
cycle:
if( выражение2 )
{
опера тор выражение 3 goto cycle;
}
В качестве "выражения!" и "выраженияЗ" можно использовать списки выражений, в которых выражения разде^лены запятыми.
Оператор for |
|
|
|
|
|
|
о Выражение_2 |
|
|||||
|
|
|
|
>j Выражение_1 |
|
||||||||
|
|
|
|
|
|
|
|
• о |
Оператор |
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
||
|
Выражение_3 |
|
|
|
|
|
|
|
|||||
|
|
Рис. 39. Синтаксическая диаграмма оператора/Ьг |
|
||||||||||
/ / |
пример |
|
namel |
|
20 ] |
= |
"Кафедра |
АВТ"^ |
|
|
|||
char |
|
|
|
|
|
|
|||||||
int |
|
|
|
kaf__name [ 20 |
]; |
|
|
|
|||||
|
|
|
1; |
|
|
|
|
|
|
|
|
||
// Копирование |
пате |
в |
kaf_name |
с использованием |
цикла |
for |
|||||||
fori |
i = |
0; |
пате[ |
i |
] |
!= |
'\0'; |
i + + |
) |
|
|
||
{ |
kaf_name |
[ |
i |
] |
= name [ |
i |
]/ |
|
|
|
|||
|
|
|
|
||||||||||
} |
name[ |
i |
] |
= |
'\0' |
/ |
|
|
|
|
|
|
|
kaf |
|
|
|
|
|
|
105
// |
Копирование |
name |
в |
kaf_name |
с использованием |
|
цикла |
while |
|||||||
// |
Инициализация |
|
|
управляющей |
переменной |
|
цикла |
|
|
||||||
1 |
- |
О/ |
|
i ] |
|
|
|
|
|
|
|
|
|
|
|
while |
( |
пате[ |
|
!= |
'\0' |
) |
|
|
|
|
|
|
|||
{ |
|
kaf__name[ |
i |
] |
= |
name |
[ i |
]; |
|
|
|
|
|
||
|
|
|
|
|
|
|
|||||||||
|
|
// |
Модификация |
|
управляющей |
переменной |
цикла |
|
|
||||||
|
|
±++; |
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kaf_name[ |
i ] |
== |
'\0'/ |
|
|
|
|
|
|
|
|
||||
// |
В |
качестве |
упражнения |
|
предлагается |
|
этот |
же |
фрагмент |
|
|||||
// |
|
записать |
с |
использованием |
цикла |
|
do-whlle |
|
|
||||||
// |
В |
заключение |
|
выполним |
|
копирование |
строк |
с |
использованием |
||||||
// |
|
строковой |
функции |
St |
г еру |
(подробнее |
о |
строковых |
функциях |
//будет сказано ниже)
^include |
<string.h> |
|
// Для строковых |
функций • |
strcpy |
( kaf__name, |
name |
) ; |
|
Из рассмотренного |
примера следует, что в подобных случаях |
оператор уЬг удобнее и легче для восприятия, чем операторы while и do'while. Это обусловлено тем, что все три выражения, связанные с организацией цикла (инициализация, проверка и модификация усло вия цикла) собраны вместе. За счет этого не приходится просматри вать исходный код в поисках выражений, обеспечивающих инициализацию и модификацию, как пришлось бы делать при применении операторов while и do-while.
Из примера также следует, что управляющая переменная циклических операторов после завершения соответствующих циклов сохраняет свое значение и значением этой переменной, при необходимости, можно пользоваться, как это было сделано в нашем примере.
Из синтаксической диаграммы цикла for следует также, что пустой оператор может быть использован для пропуска любого из выражений, входящих в состав этого оператора:
for( ; ; ) оператор // Бесконечный цикл
Как и для циклов while и do-while, для цикла for последова тельность передачи управления в теле цикла может быть изменена с помощью операторов break и continue (рис. 40).
106
for( выр1; выр2; вырЗ ) |
for( выр1; выр2; вырЗ ) |
{ |
{ |
break; |
continue; |
} Передача управления на «выражениеЗ» - см. эквивалентное представление
Рис. 40. Использование операторов break и continue в цикле/Ьг
Пример. Одномерный массив вещественного типа напечатать по
четыре элемента в строку по 15 позиций на элемент
JV
^define |
N100 |
|
|
|
|
// |
Размер |
массива |
|
||
^include |
<stdlo.h> |
|
// |
Для |
функций |
ввода-вывода |
|||||
{ |
|
|
|
|
|
|
|
|
|
|
|
float |
|
а[ |
N ]/ |
// |
Массив |
для печати |
|||||
printf( |
"\п" |
) ; |
|
// |
Начать |
печать |
с новой строки |
||||
for( |
int |
i |
= |
О; |
1 |
< N; |
i-f-f |
; |
|
|
|
{ |
±f( |
( |
1 |
% 4 |
) |
== 0 |
) |
printf( |
"\n" |
) ; |
|
|
|||||||||||
|
pr±ntf( |
|
"%15g", a[ |
i |
] |
) ; |
|
|
|||
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
В качестве упражнения рекрмендуем Вам запрограммировать эту же задачу с использованием циклов while и do-while.
Повторно напоминаем, что в языке C++ в блоке можно чередо вать определения объектов и операторы, но определение объекта
обязательно должно |
предшествовать его использованию в операто |
ре. В рассмотренном |
примере таким объектом является /. Область |
действия и время жизни / - от точки определения (заголовок цикла) и до конца блока (объект с автоматическим классом хранения).
Завершая рассмотрение циклических операторов, отметим, что при программировании цикла есть три возможности:
•использовать цикл/Ьг;
•использовать цикл while;
•использовать цикл do-while.
Возникает вопрос: какой из этих альтернатив следует восполь зоваться в конкретном случае? Ответ прост - лучше всего, как было показано выше, использовать цикл for, а это всегда можно сделать,
107
если заранее известно число повторений цикла. В остальных случа ях используются циклы while и do-while, причем цикл do-while сле дует применять, если требуется тело цикла выполнить не менее од ного раза.
4.11. Оператор goto и метки операторов
Операторы break и continue определялись выше как операторы с ограниченным диапазоном передачи управления. В дополнение к ним языки Си/С++ предоставляют программисту и нашумевший оператор перехода ^о/'о:
доЬо идентификатор;
Здесь "идентификатор" является именем метки, которая запи сывается в виде
идентификатор:
Строго говоря, при написании программ применение операто ра goto не является необходимым, так как любая программа может быть написана с помощью только трех элементарных конструкций, каждая из которых имеет только один вход и один выход:
•следование;
•ветвление;
•цикл.
Однако существуют ситуации (их немного), когда goto удобен, и поэтому его включили в язык Си. Из числа подобных ситуаций на зовем две.
1. Обычно оператор goto служит для передачи управления в конец функции в случае обнаружения ошибки, особенно если ошиб ки могут возникать во многих местах функции. В конце функции, куда выполняется переход по goto, выполняется обработка ошибок и возвращается значение, соответствующее наличию ошибки.
2. Другим примером целесообразного применения goto может служить выход из многократно вложенных циклов, поскольку опе ратор break осуществляет выход только из того цикла, где он ис пользован (рис. 41). В остальных случаях использовать goto не сле дует.
4.12.Упражнения для самопроверки
1.Изобразить фрагмент схемы программы, соответствующий сле дующему фрагменту Си-программы:
108
if( |
с =^ |
1 |
) |
а + + / |
else |
±f( с |
2 ) а- |
else |
±£( |
с |
=^ |
3 ) |
а += |
1/ |
|
while( выражение )
{
while( выражение )
{
lf( /* Ошибка */... ) goto loop_end;
(oop_end:
<
Рис. 41. Использование оператора go/^о для выхода из гнезда циклов
2. Записать фрагмент программы, соответствующий следующему фрагменту схемы программы (выполнить действие, противоположное предыдущему):
<^а <= Ь ^ Нет |
г:=3; |
i к |
1 Да
к:=п;
г:= 1;
3. с помощью операторов ветвлений и присваивания записать на языке Си фрагмент программы, вычисляющий величину
[ |
п+1 |
при |
1=4, |
9, |
п ^ [ |
а+Ь |
при |
i='l, 1, |
|
[ |
а-Ь |
в остальных |
случаях |
4. Пусть определена переменная
int |
к; |
Укажите, что напечатает следующий фрагмент программы:
printf( |
"\п |
%5s \ л " , |
'"*-" ) ; |
|||
for( к = |
1; |
к |
>= |
-5; |
к-- |
) |
printf( |
|
" |
%li |
%3s |
", |
к, "--" ) ; |
109