Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C_Kurs_Lekt / C_I_семестр / 04_Управляющие структуры

.pdf
Скачиваний:
11
Добавлен:
13.02.2016
Размер:
99.62 Кб
Скачать

Лысый Д.А. ЛЕКЦИЯ Управляющие операторы языка С.

1

УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ

Операторы управления работой программы называют управляющими конструкциями программы. К ним относятся:

составные операторы;

операторы выбора;

операторы циклов;

операторы перехода.

Êсоставным операторам относят собственно составные операторы и блоки (последовательность операторов, заключенная в фигурные скобки {}). Отличие блока от составного оператора - наличие определений в теле блока.

Операторы выбора - это условный оператор (if) и переключатель (switch).

Операторы циклов в языке Си трех видов - с предусловием (while), с постусловием (do) и параметрический (for).

Операторы перехода выполняют безусловную передачу управления: goto (безусловный переход), continue (завершение текущей итерации цикла), break (выход из цикла или переключателя), return (возврат из функции).

Рассмотрим эти операторы более подробно.

Êоператорам выбора (операторам управления потоком исполнения программы), относят: условный оператор (if...else) и переключатель (switch). Они служит для выбора пути выполнения программы.

Операторы выбора

Синтаксис условного оператора(полная форма):

if (выражение) оператор_1; else оператор_2;

выражение должно быть скалярным и может иметь арифметический тип или тип указателя. Если оно не равно нулю (или не пустой указатель), то условие считается истинным и выполняется оператор_1. В противном случае выполняется оператор_2. В качестве операторов нельзя использовать описания и определения.

if (x > 0) y = 1; else y = 2;

В качестве операторов могут быть составные операторы и блоки:

if (x > 0) { y = 1; z = 5 ; } else { x = 2; z = 3; }

Допустима сокращенная форма условного оператора, в которой отсутствует else è оператор_2. if (выражение) оператор_1; else оператор_2;

Âэтом случае при ложности (равенстве нулю) проверяемого условия никакие действия не выполняются.

Âсвою очередь, оператор_1 и оператор_2 могут быть условными, что позволяет организовать цепочку проверок условий любой глубины вложенности. При этом могут возникать ошибки неоднозначного сопоставления if è else. Синтаксис языка предполагает, что при вложениях условных операторов каждое else соответствует ближайшему к нему предшествующему if. В тексте соответствие if è else удобно показывать(не задавать!) с помощью отступов. В качестве примера неверного толкования этого правила в документации [5] приводится такой пример:

if (х == l)

if (ó == 1) printf("х равно 1 и ó равно 1"); else printf(“х не равно 1");

При х, равном 1, и у, равном 1, совершенно справедливо печатается фраза "х равно 1 и ó равно 1". Однако фраза "х не равно 1" может быть напечатана только при х, равном 1, и при у, не равном 1, т.к. else относится к ближайшему if. Внешний условный оператор, где проверяется х=1, является сокращенным и в качестве оператора_1 включает полный условный оператор, в котором проверяется условие у=1. Таким образом, проверка этого условия выполняется только при х, равном 1! Простейшее правильное решение этой микрозадачи можно получить, применив фигурные скобки, т.е. построив составной оператор. Нужно фигурными скобками ограничить область действия внутреннего условного оператора, сделав его неполным. Тем самым внешний оператор превратится в полный условный:

if (х == l)

{if (ó == 1) printf("х равно 1 и ó равно 1");} else printf(“х не равно 1");

Переключатель является наиболее удобным средством для организации мультиветвления. Синтаксис переключателя таков:

switch (переêлячающее_выражение)

{case êонстантное_выражение_1: операторы_1; case êонстантное_выражение_2: операторы_2; case êонстантное_выражеêие_n: операторы_n;

default: операторы;

}

Управляющая конструкция switch передает управление к тому из помеченных с помощью case операторов, для которого значение константного выражения совпадает со значением переключающего выражения. Переклю- чающее выражение должно быть целочисленным или его значение приводится к целому. Значения константных выражений, помещаемых за служебными словами case, приводятся к типу переключающего выражения. В одном переключателе все константные выражения должны иметь различные значения, но быть одного типа. Любой из операторов, помещенных в фигурных скобках после конструкции switch (...), может быть помечен одной или несколькими метками вида

саsе êонстантное_выражение:

Если значение переключающего выражения не совпадает ни с одним из константных выражений, то выполняется переход к оператору, отмеченному меткой default:. В каждом переключателе должно быть не больше одной метки default.. В случае отсутствия метки default при несовпадении переключающего выражения ни с одним из константных выражений, помещаемых вслед за case, в переключателе не выполняется ни один из операторов.

Если не предусмотрены переходы или выход из переключателя, то в нем последовательно выполняются все операторы, начиная с той метки, на которую передано управление. Пример программы с переключателем:

/* печать названий нечетных целых цифр, не меньших заданной */ #include <stdio.h>

Лысый Д.А. ЛЕКЦИЯ Управляющие операторы языка С.

2

()

{ int ic;

printf(“\nВведите любóю десятичнóю цифрó: "); scanf(“%d”,&ic);

switch (ic)

{case 0: case 1: printf("один, "); case 2: case 3: printf("три, "); case 4: case 5: printf("пять, "); case 6: case 7: printf("семь, ");

case 8: case 9: printf("девять.");

break; /* выход из переêлючателя */ default: printf("Ошибêа! Это не цифра!");

}/* Конец переêлючателя */

}/* Конец проãраммы */

Результаты двух выполнения программы:

Введите любóю десятичнóю цифрó: 4 <Enter> пять, семь, девять Введите любóю десятичнóю цифрó: z <Enter> Ошибêа! Это не цифра!

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

Операторы цикла

Операторы цикла задают многократное выполнение операторов тела цикла. Определены три формы оператора цикла:

цикл с предусловием:

while (выражение-óсловие) тело_циêла

цикл с постусловием:

do

тело_циêла

while (выражение-óсловие);

итерационный цикл:

for (инициалиэация_циêла; выражение-óсловие; списоê__выражеêий) тело_циêла

Тело_циêла не может быть описанием или определением. Это либо отдельный (в том числе пустой) оператор, который всегда завершается точкой с запятой, либо составной оператор, либо блок (заключаются в фигурные скобки). Выражение-óсловие - это во всех операторах скалярное выражение (чаще всего отношение или арифметическое выражение), определяющее условие продолжения выполнения итераций (если его значение не равно нулю).

Прекращение выполнения цикла возможно в следующих случаях:

нулевое значение проверяемого выражения-условия;

выполнение в теле цикла оператора передачи управления (break, goto, return) за пределы цикла.

Оператор while (оператор "повторять пока (истинно условие)") называется оператором цикла с предуслови-

åì.

while (выражение-óсловие) тело_циêла

При входе в цикл вычисляется выражение-условие. Если его значение отлично от нуля, то выполняется тело_циêла. Затем вычисление выражения-условия и выполнение операторов тела_циêла повторяются последовательно, пока значение выражения-условия не станет равным 0.

Оператором while удобно пользоваться для просмотра всевозможных последовательностей, если в конце каждой из них находится заранее известный признак. Например, по определению, строка есть последовательность символов типа char, в конце которой находится нулевой символ. Следующая функция подсчитывает длину строки, заданной в качестве параметра:

int length

(char

*stroka) {

int

len =

0;

 

while

(*stroka++)

len++;

return

len;

 

 

)

 

 

 

В качестве проверяемого выражения-условия часто используются отношения. Например, следующая последовательность операторов вычисляет сумму квадратов первых к натуральных чисел (членов натурального ряда):

int i = 0, s = 0;

while (i

< К)

s += ++i *

i;

Если в выражении-условии нужно сравнивать указатель с нулевым значением (с пустым указателем), то сле-

дующие три проверки эквивалентны:

while (point != NULL) ...

while (point) ...

while (point != 0) ...

Следующий оператор while(1); обеспечивает бесконечное выполнение пустого оператора в теле цикла. Та-

кой цикл может быть прекращен только за счет событий, происходящих вне потока операций, явно предусмотрен-

ных в программе.

Оператор do (оператор "повторять") называется оператором цикла с постусловием. Он имеет следующий вид:

do

тело_циêла while (выражение-óсловие);

При входе в цикл do обязательно выполняется тело_циêла (по крайней мере один раз). Затем вычисляется выражение-óсловие и, если его выражение не равно 0, вновь выполняется тело_циêла. При обработке некоторых последовательностей применение цикла с постусловием оказывается удобнее, чем цикла с предусловием. Это бывает

Лысый Д.А. ЛЕКЦИЯ Управляющие операторы языка С.

3

в тех случаях, когда обработку нужно заканчивать не до, а после появления концевого признака. Например, следующая функция переписывает заданную строку (указатель star) в другую, заранее подготовленную строку (nov):

void сopy_ste{char *star, char *nov) { do *nov = *star++;

while (*nov++); )

К выражению-условию требования те же, что и для цикла while с предусловием - оно должно изменяться при итерациях либо за счет операторов тела цикла, либо при вычислениях.

Бесконечный цикл: do ; while(l);

Оператор итерационного цикла for имеет формат:

for (иêициализация_циêла; выражение-óсловие; списоê_выражений) тело_циêла

Здесь инищиализация_циêла - последовательность определений, описаний и выражений, разделяемых запятыми. Все выражения, входящие в инициализацию цикла, вычисляются только один раз при входе в цикл. Чаще всего здесь устанавливаются начальные значения счетчиков и параметров цикла, выражение-óсловие такое же, как и в циклах while и do. если оно равно 0, то выполнение цикла прекращается, В случае отсутствия выраженияусловия следующий за ним разделитель "точка с запятой" сохраняется. При отсутствии выражения-условия предполагается, что его значение всегда истинно. При отсутствии инициализации цикла соответствующая ему точка с запяток сохраняется, Выражения из списоêа_выражений вычисляются на каждой итерации послее выполнения операторов тела цикла и до следующей проверки выражения-условия. Тело_ циêла может быть отдельным оператором, составным оператором и пустым оператором.

Следующие операторы for иллюстрируют разные решения задачи суммирования квадратов первых к членов натурального ряда:

for ( i = 1, s = 0; i <= K; i++ ) s += i * i; for (i = 0, s = 0; i <= K; s += ++i * i ) ; for (i = 0, s = 0; i <= K; ) s += ++i * i;

for (i = 0, a = 0; i <= K; ) { j = ++i; s += j * j; }

Во втором операторе тело_циêла - пустой оператор. В третьем отсутствует списоê_выражений. Во всех операторах в инициализации_циêлов определены (и инициализированы) целые переменные.

Еще раз проследим последовательность выполнения итерационного цикла for. Определяются объекты и вы- числяются выражения, включенные в инициализацию_циêла. Вычисляется выражение-óсловие. Если оно отлично от нуля, выполняются операторы тела_циêла. Затем вычисляются выражения из списка выражений, вновь вычисляется выражение-условие и проверяется его значение. Далее цепочка действий повторяется.

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

for( ; ;); for( ; 1; );

Разрешено и широко используется вложение любых циклов в любые циклы

Операторы передачи управления

Оператор безусловного перехода имеет вид: goto идентифиêатор;

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

Принятая в настоящее время дисциплина программирования рекомендует либо вовсе отказаться от оператора goto, либо свести его применение к минимуму и строго придерживаться следующих рекомендаций]:

не входить внутрь блока извне;

не входить внутрь условного оператора, т.е. не передавать управление операторам, размещенным после служебных слов if или else;

не входить извне внутрь переключателя (switch);

не передавать управление внутрь цикла.

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

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

Оператор возврата из функции имеет вид:

return выражение;

или просто

return;

Выражение, если оно присутствует, может быть только скалярным. Например, следующая функция вычисляет и возвращает куб значения своего аргумента:

float cube (float z) { return z*z*z;}

Выражение в операторе return не может присутствовать в том случае, если возвращаемое функцией значе-

Лысый Д.А. ЛЕКЦИЯ Управляющие операторы языка С.

4

ние имеет тип -void.

Оператор break служит для принудительного выхода из цикла или переключателя. Определение "принудительный" подчеркивает безусловность перехода. Например, в случае цикла не проверяются и не учитываются условия дальнейшего продолжения итераций. Оператор break прекращает выполнение оператора цикла или переключа- теля и осуществляет передачу управления (переход) к следующему за циклом или переключателем оператору. При этом в отличие от перехода с помощью goto оператор, к которому выполняется передача управления, не должен быть помечен. Оператор break нельзя использовать нигде, кроме циклов и переключателей.

Необходимость в использовании оператора break в теле цикла возникает, когда условия продолжения итераций нужно проверять не в начале итерации (циклы for, while), не в конце итерации (цикл do), а в середине тела цикла. В этом случае тело цикла может иметь такую структуру:

{ операторы

if (óсловие) break;

операторы )

Циклы и переключатели могут быть многократно вложенными. Однако следует помнить, что оператор break позволяет выйти только из самого внутреннего цикла или переключателя. Т.е. при многократном вложении циклов и переключателей оператор break не может вызвать передачу управления из самого внутреннего уровня непосредственно на самый внешний. Например, при решении задачи поиска в матрице хотя бы одного элемента с заданным значением удобнее всего пользоваться не оператором break, а оператором безусловной передачи управления (goto):

...

 

 

for (i

= 0; i < n; i++)

for

(j = 0; j < m; j++)

{ if {A[i][j]

== x) goto success;

 

...

/* Действия при отсóтствии элемента в матрице */

}

/* Конец циêла */

success:

printf("\nЭлемент х найден. Строêа i = %d столбец j = %d ",i,j);

В качестве примера, когда при вложении циклов целесообразно применение оператора break, рассмотрим задачу вычисления произведения элементов строки матрицы. В данном случае вычисление произведения элементов можно прервать, если один из сомножителей окажется равным 0, Возможный вариант реализации может быть таким:

...

for (i = 0; i < n; i++) /* Перебор строê матрицы */ /* Перебор элементов строêи */

for (j = 0, p[i] = 1; j < m; j++)

if (A[i][j] == 0.0) /* Обнарóжен нóлевой элемент */ { p[i] = 0,0; break; }

else p[i] *= A[i][j];

При появлении в строке нулевого элемента оператор break прерывает выполнение только внутреннего цикла, однако внешний цикл перебора строк всегда выполнится для всех значений i от 0 до n - 1.

Оператор continue употребляется только в операторах цикла. С его помощью завершается текущая итерация и начинается проверка условия дальнейшего продолжения цикла, т.е. условий начала следующей итерации. Для

объяснений действия оператора continue рекомендуется рассматривать следующие три формы основных операторов цик-

ëà:

 

 

 

 

 

while (foo)

do

for

(;foo;)

{

...

{

...

{

...

contin:

 

contin:

contin;

}

)

 

while (foo);

}

 

В каждой из форм многоточием обозначены операторы тела цикла,

Вслед за ними размешен пустой оператор с меткой contin. Если среди операторов тела цикла есть оператор continue и он выполняется, то его действие эквивалентно оператору безусловного перехода на метку contin.

Типичный пример использования оператора continue: подсчитать среднее значение только положительных элементов одномерного массива:

for (s = 0.0, k = 0, i = 0; i < n; i++) { if (x[i] <= 0.0) continue;

k++;

s += x[i];

}

if (k > 0) s = s/k;