Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

86

Г л а в а 6

 

 

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

Чтобы проиллюстрировать возможности циклов и показать еще несколько приемов программирования на C# , рассмотрим задачу об оценке машинного нуля относительно заданного числового значения.

В предыдущей главе, посвящённой связи типов языка с классами .NET Framework, мы узнали, что для классов вещественных чисел введено константное статическое поле Epsilon, значение которого – наименьшее отличное от нуля значение, которое может принимать переменная заданного вещественного типа. При решении вычислительных задач иногда требуется использовать не минимально допустимые значения вещественных чисел, а число, которое пренебрежимо мало относительно другого числа, принятого за базовое. Иногда это малое число называют машинным нулем, относительно базовой величины.

Иллюстрируя применение операторов цикла, напишем программу, позволяющую оценить машинный нуль относительно вводимого пользователем значения.

/ 06_03.cs циклы и машинный нуль. using System;

class Program

{

static void Main()

{

double real, estimate, zero; do

Console.Write("Введите положительное число: "); while (!double.TryParse(Console.ReadLine(), out

real)||real <= 0);

for (estimate = 0, zero = real; estimate != real; zero /= 2.0) estimate = real + zero;

Console.WriteLine("Машинный нуль: " + zero);

}

}

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

Введите положительное число: -16<ENTER> Введите положительное число: двадцать<ENTER> Введите положительное число: 23<ENTER>

Машинный нуль: 6,38378239159465E-16

Операторы

87

 

 

В программе два цикла. Цикл с постусловием служит для “наблюдения” за правильностью ввода исходного числового значения. От пользователя требуется, чтобы он ввел положительное число. Метод TryParse() класса double анализирует вводимую пользователем строку символов. Если она корректно представляет вещественное число, то метод TryParse() возвращает значение true и присваивает значение введенного числа аргументу real. Выражение_условие цикла do (размещенное в скобках после while), истинно, если метод TryParse() вернет значение false или значение переменной real не положительное. В этом случае цикл повторяется, и пользователь повторно видит приглашение «Введите положительное число».

Как только пользователь введет положительное значение переменной real, начинает выполняться второй цикл. В конце каждой итерации вдвое уменьшается значение переменной zero. Цикл выполняется до тех пор, пока сумма значения real с zero не равна значению real. Равенство достигается в том случае, когда значение zero становится пренебрежимо малой величиной относительно real. Таким образом, достигнутое значение zero можно считать машинным нулем относительно real.

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

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

Кроме оператора безусловного перехода (иначе – оператора безусловной передачи управления goto), к операторам передачи управления относят: оператор возврата из метода return; оператор выхода из цикла или переключателя break; оператор перехода к следующей итерации цикла continue; оператор посылки исключения throw.

Оператор return подробно рассмотрим, перейдя к методам (функциям). Оператор throw требует хорошего понимания механизма генерации и обработки исключений, поэтому знакомство с ним нужно отложить. Остановимся сейчас на операторах break, continue и goto.

88

Г л а в а 6

 

 

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

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

{ операторы

if (условие) break; операторы

}

Например, если начальные значения целых переменных i, j таковы, что i < j, то следующий цикл определяет наименьшее целое, не меньшее их среднего арифметического:

while (i < j) { i++; if (i == j) break; j--; }

Для i == 0, j == 3 результат i == j == 2 достигается при выходе из цикла с помощью оператора break. (Запись i == j == 2 не в тексте программы означает равенство значений переменных i, j и константы 2.) Для i == 0 и j == 2 результат i == j == 1 будет получен при естественном завершении цикла.

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

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

Операторы

89

 

 

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

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

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

goto идентификатор;

где идентификатор – имя метки оператора, расположенного в той же функции, где используется оператор безусловного перехода.

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

●●не входить внутрь блока извне (компилятор C# этого не позволит – сообщит об ошибке);

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

●●не входить извне внутрь переключателя (компилятор C# сообщит об ошибке);

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

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

90

Г л а в а 6

 

 

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

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

while (foo) {

do {

for (;foo;) {

...

...

...

contin: ;

contin: ;

contin: ;

}

} while (foo);

}

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

Приведем пример использования оператора continue. Вводя с клавиатуры последовательность вещественных чисел, требуется подсчитать сумму только положительных. Окончанием ввода последовательности следует считать ввод нулевого значения.

// 06_04.cs оператор перехода к следующей итерации using System;

class Program

{

static void Main() { double sum = 0, x;

do { Console.Write("x = ");

x = Double.Parse(Console.ReadLine()); if (x < 0) continue;

sum += x;

}

while (x != 0); Console.WriteLine("Сумма = "+sum);

}

}

Операторы

91

 

 

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

x = 2<ENTER> x = 4<ENTER> x = 6<ENTER> x = 0<ENTER>

Сумма = 8

Напомним, что без обработки исключений применение метода Parse() делает программу незащищенной от синтаксических ошибок во вводимых исходных данных.

6.6. Переключатель

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

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

{ case константное_выражение_1: операторы_1; break;

case константное_выражение_2: операторы_2; break;

...

case константное_выражение_n: операторы_n; default: операторы;

break;

}

В переключателях используют 4 служебных слова: switch, case, break, default и обязательные фигурные скобки, ограничивающие тело переключателя. Конструкция

case константное_выражение:

называется меткой переключателя. Константное выражение может быть целочисленным, может быть символом, строкой или элементом перечисления. (О перечислениях речь пойдет в главе 15).

Заголовок, т. е. управляющая конструкция

switch (переключающее выражение),

передает управление к тому из помеченных с помощью case операторов, для которого значение константного выражения

92

Г л а в а 6

 

 

совпадает со значением переключающего выражения. Значение переключающего выражения должно быть целочисленным или иметь тип char, string, тип перечисления, или приводиться к целому. Переключающее выражение не может иметь вещественный тип и не может быть десятичным (decimal).

Метка переключателя вводит ветвь или раздел переключателя – последовательность операторов, завершаемую оператором break или goto case i, или goto default. (В приведённом формате переключателя указаны только операторы break, так как они используются наиболее часто.) Назначение оператора break – завершить выполнения переключателя. Операторы goto case i или goto default обеспечивают переход к другой “ветке” переключателя.

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

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

В качестве примера приведем программу перевода оценки в баллах при десятибалльной шкале в аттестационную (четырехбалльную) оценку.

Соответствие: 1, 2, 3 балла – неудовлетворительно; 4, 5 – удовлетворительно; 6, 7 – хорошо; 8, 9, 10 – отлично.

// 06_05.cs переключатель using System;

Операторы

93

 

 

class Program

{

static void Main()

{

int ball; // оценка в баллах: do

Console.Write("Введите оценку в баллах: "); while (!int.TryParse(Console.ReadLine(), out ball)

|| ball <= 0 || ball > 10) ; switch (ball)

{

case 1: case 2: case 3:

Console.WriteLine("Неудовлетворительно"); break;

case 4: case 5:

Console.WriteLine("Удовлетворительно"); break;

case 6: case 7:

Console.WriteLine("Хорошо"); break;

case 8: case 9: case 10:

Console.WriteLine("Отлично"); break;

default: Console.WriteLine("Ошибка в данных"); break;

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

}

}

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

Введите оценку в баллах: гг<ENTER> Введите оценку в баллах: -9<ENTER> Введите оценку в баллах: 0<ENTER> Введите оценку в баллах: 9<ENTER> Отлично

94

Г л а в а 6

 

 

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

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

switch (n)

// Переключатель с ошибками

{ char d = 'D';

// Никогда не обрабатывается

case 1: double f = 3.14; // Обрабатывается только для n == 1 break;

case 2:… if ((int)d != (int)f)// Ошибка: d и/или f не определены break;

... }

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

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

// 06_06.cs переключатель с внутренними переходами using System;

class Program

{

static void Main() { int ic = 5;

string line = ""; switch (ic)

{

case 0:

case 1: line += "one, "; goto case 2;

case 2:

case 3: line += "three, "; goto case 4;

case 4:

case 5: line += "five, "; goto case 6;

Операторы

95

 

 

case 6:

case 7: line += "seven, "; goto case 8;

case 8:

case 9: line += "nine."; break;

default: line = "Error! It is not digit!"; break;

}

Console.WriteLine("Цифры: " + line);

}

}

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

Цифры: five, seven, nine.

Контрольные вопросы

1.Каково назначение оператора в программах на C#?

2.Перечислите встроенные операторы языка C#.

3.Каков обязательный признак отличного от блока оператора в C#?

4.Что такое оператор-выражение?

5.Где разрешено размещать пустой оператор?

6.Что такое метка?

7.Дайте определение блока.

8.Какими правилами определяются вход в блок и выход из него?

9.Назовите операторы выбора (операторы ветвлений).

10.Какие операторы не могут входить в условный оператор?

11.Что такое сокращенная форма условного оператора?

12.Как устанавливается соответствие между if и else при вложениях условных операторов?

13.Назовите виды операторов циклов в C#.

14.Какой оператор не может быть телом цикла?

15.Какой тип имеет выражение-условие в операторе цикла?

16.Сколько элементов в заголовке цикла общего вида (цикла for) и как они разделяются?

17.Что такое инициализатор цикла общего вида (цикла for)?

18.Когда вычисляется завершающее выражение цикла for?

19.Укажите область существования объектов, объявленных в инициализаторе цикла for.

20.Как выполняется вложение циклов?

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]