Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Программирование в Visual С++ 2005 12 страница




 

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

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

 

 


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

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

 


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

 


Вы можете использовать операцию запятой, чтобы включить несколько счетчи-
ков в цикл for. Ниже показан пример.

Описание полученных результатов

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


Понятно, что таким образом в каждый из разделов for можно вставить столько вы-
ражений, сколько понадобится.

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

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

При каждом увеличении i на 1 значение power удваивается за счет сложения с са-
мим собой. Это порождает последовательность чисел, представляющих степени двой-
ки. Результат работы программы выглядит следующим образом.

Манипулятор setw, с которым вы познакомились в предыдущей главе, использует-
ся для симпатичного выравнивания столбцов цифр. Чтобы использовать setw () без
квалифицированного имени, в программу включен заголовочный файл <iomanip> и
добавлено объявление using для имен из пространства std.



Если вы пропустите второе управляющее выражение, которое специфицирует
проверочное условие цикла for, то предполагается, что это условие всегда будет
true, поэтому цикл будет продолжаться бесконечно, если только вы не предусмотри-
те какой-то другой способ выхода из него. На самом деле при желании вы можете
пропустить все управляющие выражения в скобках после for. Это может показаться
не слишком полезным, однако, в действительности все как раз наоборот. Довольно
часто возникают ситуации, когда необходимо выполнить цикл некоторое число раз,
но вы заранее не знаете, сколько именно итераций понадобится. Рассмотрим следую-
щий пример.



Описание полученных результатов

Данная программа вычисляет среднюю величину произвольного числа значений.
После ввода каждого числа пользователь должен указать, желает ли он ввести еще
одно значение, введя один символ — у или п. Типичный сеанс работы с этой програм-
мой будет выглядеть так:

 

 


После объявления и инициализации переменных, которые вы собираетесь ис-
пользовать, запускается цикл for без управляющих выражений, а, следовательно, и
без гарантий завершения. Непосредственно следующий за этим блок — субъект цикла,
который должен повторяться.

Блок цикла выполняет следующие базовые действия.

□ Читает значение.

□ Добавляет прочитанное из cin значение к sum.

□ Проверяет, желаете ли вы продолжить вводить значения.

Первое действие внутри блока приглашает вас ввести число, и читает введенное
значение в переменную value. Введенное значение добавляется в sum, после чего за-
дается вопрос — желаете ли вы продолжить ввод, и предлагается ввести 1 п1, если не
желаете. Введенный символ помещается в переменную indicator для последующей
проверки на равенство 'п' или 'N' в операторе if. Если равенства нет, цикл про-
должается, в противном случае выполняется break. Эффект от break в цикле подо-
бен тому, что он имеет в контексте оператора switch. В данном случае он приводит
к немедленному выходу из цикла с передачей управления оператору, следующему за
закрывающей фигурной скобкой блока цикла.


162 Глава 3

 

И, наконец, выполняется вывод количества введенных значений и их средней ве-
личины, которая вычисляется делением sum на i. Конечно, было бы неплохо приве-
сти i к типу double перед вычислением, если вы помните разговор о приведениях в
главе 2.

Использование оператора continue

Имеется еще один оператор помимо break, служащий для управления циклом —
continue. Записывается он очень просто:

Выполнение continue внутри цикла немедленно начинает следующую итерацию
цикла, пропуская операторы тела цикла, которые оставалось выполнить в текущей
итерации. Я могу продемонстрировать, как это работает, на следующем примере:

 


Цикл читает 10 чисел с намерением получить произведение всех введенных зна-
чений. Оператор if проверяет, не был ли введен 0, и если это так, то выполняется
continue, чтобы сразу перейти к следующей итерации. Это делается для того, чтобы
произведение введенных чисел не обратилось в ноль при первом же вводе нулевого
значения. Понятно, что если ноль будет введен на последней итерации, цикл просто
завершится. Безусловно, существуют и другие способы достичь того же результата, но
continue представляет очень удобное средство, в частности, для сложных циклов,
где вам может понадобиться пропускать остаток текущей итерации, начиная с разных
точек его тела.

Влияние операторов break и continue на логику цикла for иллюстрирует
рис. 3.4.

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




Описание полученных результатов

Программа начинается с нескольких объявлений using для имен некоторых но-
вых манипуляторов, управляющих представлением вывода.

Цикл управляется переменной capital типа char, которая объявлена вместе с
переменной small в выражении инициализации. Обе переменных подвергаются ин-
крементированию в третьем управляющем выражении цикла, так что capital изме-
няется от 'А' до 1Z', а значение small — соответственно от 'а1 до 1 z1.

Тело цикла содержит единственный оператор вывода, растянутый на семь строк.
Первая из них:

начинает новую строку на экране.
Следующие три строки таковы:

 


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

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

Затем в поток вставляется манипулятор dec, что заставляет его отобразить следу-
ющее значение в десятичном виде. По умолчанию переменная типа char интерпрети-
руется потоком как символ, а не как числовая величина. Вы отправляете в поток пе-
ременную capital типа char как числовое значение путем приведения его значения
к типу int операцией staticcasto (), которая вам знакома по предыдущей главе.

Значение small выводится точно таким же образом в следующих трех строках
оператора вывода:

 

 


В результате программа генерирует следующий вывод:


Решения и циклы 165

Счетчики с плавающей точкой

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

 


Этот фрагмент кода вычисляет значение а*х+Ь для значений х от 0. О до 2. О с
шагом 0.25; однако вы должны быть осторожны, применяя в циклах счетчик с пла-
вающей точкой. Многие десятичные значения не могут быть представлены точно в
двоичной форме с плавающей точкой, поэтому в аккумулируемых значениях может
накапливаться погрешность. Это значит, что вы не должны кодировать цикл for так,
чтобы его завершение зависело от достижения счетчиком с плавающей точкой точ-
ного значения. Например, следующий неправильно спроектированный цикл никогда
не завершится:

 

 


Казалось бы, этот цикл должен отобразить значения х, изменяя их от 0.0 до 1.0;
однако шаг 0. 2 не может быть представлен точно в формате с плавающей точкой,
поэтому значение х никогда не будет точно равно 1.0. Следовательно, второе управ-
ляющее циклом выражение всегда будет true, а потому цикл будет продолжаться бес-
конечно.


166 Глава 3

 

Цикл while

Второй тип циклов С++ — цикл while. В то время как цикл for предназначен глав-
ным образом для повторения оператора или блока операторов определенное количе-
ство раз, цикл while служит для выполнения оператора или блока до тех пор, пока
указанное условие остается истинным. Общая форма цикла while показана ниже.

 

 


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

Логика цикла while представлена на рис. 3.5.


 


 
 


 




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


 


       
   


 
 

Описание полученных результатов

При том же вводе, что и ранее, эта программа генерирует такой же вывод. Один
оператор был изменен, другой добавлен — они выделены полужирным в исходном
коде. Оператор цикла for заменен while, и исключена проверка indicator в if, по-
скольку теперь эту функцию выполняет условие цикла while. Вы должны инициали-
зировать indicator значением ' у' вместо ' п1, как было раньше. Иначе цикл while
прервется немедленно. До тех пор, пока условие while возвращает true, цикл будет
продолжаться.

Вы можете поместить в условие цикла while любое выражение, которое в результа-
те дает true или false. Данный пример можно усовершенствовать, если сформулиро-
вать условие цикла так, чтобы для продолжения цикла можно было вводить 1Y1 наряду
с 1 у *. Для этого потребуется модифицировать оператор while следующим образом:

 


Можно также создать потенциально бесконечный цикл while, используя условие,
которое всегда true. Это можно написать так:

 

 


Можно также записать управляющее выражение цикла как целочисленное значе-
ние 1, которое будет преобразовано в значение true типа bool. Естественно, здесь
также остается в силе то же требование, что предъявляется к бесконечным циклам
for, а именно: необходимо предусмотреть какой-нибудь способ выхода из цикла вну-
три его тела. Вы увидите в главе 4 другие примеры применения цикла while.

 

Цикл do-while

Цикл do-while подобен циклу while в том, что он выполняется до тех пор, пока
указанное условие остается истинным. Главное отличие его состоит в том, что здесь
условие проверяется в конце цикла — что отличает его от циклов for и while, где


168 Глава 3

 

оно проверяется в начале. Как следствие, оператор внутри цикла do-while всегда вы-
полняется, по меньшей мере, один раз. Общая форма цикла do-while следующая:

Логика этой формы цикла показана на рис. 3.6.


 

Рис. 3.6. Оператор цикла do-while

Вы можете заменить цикл while в последней версии программы вычисления сред-
него циклом do-while:

 


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

Вложенные циклы

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


 
 

Эффект от вложения одного цикла в другой можно увидеть на примере вычисле-
ния значений по простой формуле. Факториал целого числа — это произведение всех
целых чисел от 1 и до заданного включительно. Поэтому факториал 3, например —
это 1, умноженное на 2 и на 3, что равно 6. Следующая программа вычисляет факто-
риал целых чисел, которые вводит пользователь:

 

 


Если вы скомпилируете и выполните этот пример, он будет работать примерно
так:

Описание полученных результатов

Значения факториала возрастают очень быстро. Фактически, 12— наибольшее
входное значение, для которого данный пример выдает правильный результат.
Факториал 13 на самом деле равен 6 227 020 800, а не 1 932 053 504, как сообщает про-


170 Глава 3

 

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

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

Внешний из двух циклов, цикл do-while, управляет завершением программы. До
тех пор, пока вы вводите ' у1 или ' Y1 в ответ на запрос о продолжении, програм-
ма продолжает вычислять значения факториала. Факториал целых чисел вычис-
ляется во вложенном цикле for. Он выполняется, value раз умножая переменную
factorial (чье начальное значение равно 1), на последовательные целые числа — от
2 до value.

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

 


Ниже показан вывод этого примера.


 
 

Описание полученных результатов

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

Вложенный цикл генерирует основное содержимое таблицы. Внешний цикл повто-
ряется по разу для каждой строки, поэтому здесь i — номер столбца. Оператор вывода

 

 


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

 

 


Этот цикл выводит значения i*j, соответствующие произведению текущего значе-
ния строки i на номер столбца j, изменяя j от 1 до size. Таким образом, при каждой
итерации внешнего цикла внутренний цикл проходит size итераций. Полученные
значения размещаются таким же образом, как заголовки столбцов.

Когда завершается внешний цикл, выполняется return для завершения программы.

 

Программирование на C++/CLI

Все, о чем я говорил в этой главе, в равной мере относится к программам C++/CLI.
Для того чтобы просто проиллюстрировать это, мы можем рассмотреть некоторые
примеры консольных программ CLR, которые демонстрируют кое-что из того, что
вы изучили до сих пор в этой главе. Ниже представлена программа CLR, представля-
ющая собой вариацию на тему ЕхЗ_01.


172 Глава 3

 

 

Практическое занятие

Программа CLR, использующая вложенные
операторы if

Создайте консольную программу с кодом по умолчанию и модифицируйте функ-
цию main () следующим образом:

Как всегда, новые строки кода выделены полужирным.

Описание полученных результатов

Логика этой программы в точности повторяет логику примера ЕхЗ_01 (фактиче-
ски все операторы те же самые, за исключением операторов вывода и объявления
letter). Я изменил тип этой переменной на wchar_t, поскольку тип Char име-
ет несколько дополнительных возможностей, о которых я еще упомяну. Функция
Console::Read () читает одиночный символ с клавиатуры. Поскольку для вывода на-
чального приглашения применяется Console::Write (), символ новой строки не вы-
водится, так что вы можете вводить символ в той же строке, что и приглашение.

Среда.NET Framework предлагает свои собственные функции в классе Char для
преобразования кодов символов в верхний и нижний регистр. Это функции Char::
ToLower () и Char:: ToUpper (), и вы помещаете преобразуемый символ в качестве
аргумента между скобками при вызове этих функций. Например:

 

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

 

Класс Char также предлагает функции IsUpper () и IsLower (), проверяющие ре-
гистр символа. Вы передаете проверяемый символ этим функциям в виде аргумен-


Решения и циклы 173

 

та, а они возвращают значение bool в качестве результата. Это можно использовать,
чтобы закодировать main () несколько иначе.

 

 


Применение этих функций заметно упрощает код. После преобразования letter
в верхний регистр вы проверяете преобразованное значение на вхождение в диапа-
зон от 'А' до 1Z1. Если оно входит в этот диапазон, выдается сообщение, зависящее
от выражения условной операции, которое формирует второй аргумент для функции
WriteLine (). Условная операция возвращает "заглавную", если letter относится
к верхнему регистру, и "прописную", если к нижнему, а результат вставляется в пози-
цию выходной строки, отмеченную форматной строкой {0}.

Теперь посмотрите на другой пример CLR, использующий функцию Console::
ReadKey () в цикле и раскрывающий немного подробнее класс ConsoleKeylnf о.


 




Создайте консольную программу CLR и добавьте следующий код в main ():

 

 

Ниже показан результат работы этой программы.

 

'


Конечно, некоторые комбинации клавиш не представляют никакого отображаемо-
го символа, поэтому в этих случаях никакие символы и не выводятся. Программа так-
же завершается по нажатию <Ctrl+C>, потому что операционная система распознает
эту комбинацию как команду завершения программы.

Описание полученных результатов

Нажатия клавиш проверяются в цикле do-while, и этот цикл продолжается до
тех пор, пока не будет нажата клавиша <Esc>. Внутри цикла вызывается функция
Console::ReadKey (), и ее результат помещается в переменную keyPress, имею-
щую тип ConsoleKeylnf о. Класс ConsoleKeylnf о имеет три свойства, обратившись
к которым, можно получить вспомогательную информацию относительно нажатой
клавиши или комбинации клавиш. Свойство Key идентифицирует нажатую клави-
шу, свойство Key Char представляет код клавиши в кодировке Unicode, а свойство
Modifiers — это битовая комбинация констант ConsoleModifiers, представляющих
состояние клавиш Shift, Alt и Ctrl. ConsoleModif iers — перечисление, опреде-
ленное в библиотеке System, и константы, определенные в нем, имеют имена Alt,
Shift и Control.

Как можно видеть из аргументов функции WriteLine () в последнем операторе
вывода, для доступа к свойству объекта необходимо поместить наименование свой-
ства следом за именем объекта, отделив его точкой. Здесь точка — это операция до-
ступа к члену. Чтобы получить доступ к свойству KeyChar объекта keyPress, вы
должны написать keyPress.KeyChar.

Программа работает очень просто. Внутри цикла вызывается функция ReadKey ()
для чтения нажатия клавиши, и ее результат помещается в переменную keyPress.
Далее с помощью функции Write () начальная часть вывода помещается в командную
строку. Поскольку в этом случае не выводится символ новой строки, последующий вы-
вод продолжается в той же строке. Затем выполняется проверка свойства Modifiers
на предмет того, больше ли оно нуля. Если это так, значит, были нажаты модифици-
рующие клавиши, и об этом выводится сообщение, в противном случае информация
о модифицирующих клавишах пропускается. Возможно, вы помните, что перечисли-
мые константы C++/CLI являются объектами, которые нужно явно привести к цело-
численном типу, прежде чем использовать их как числовые значения — отсюда при-
ведение к int в выражении if.

Интересен вывод Modifiers. Как видно из результирующего вывода программы,
когда нажато более одной модифицирующей клавиши, вы получаете информацию о
них всех в одном операторе вывода. Это потому, что перечисление Modifiers опреде-
лено с атрибутом FlagsAttribute, который указывает на то, что этот перечислимый
тип представляет набор отдельных битовых флагов. Благодаря этому переменные
перечислимого типа могут состоять из нескольких флагов, объединенных вместе ло-
гическим И, а индивидуальные флаги распознаются и выводятся функциями Write ()
и WriteLine().


Цикл продолжается до тех пор, пока истинно условное выражение keyPress.Key
!= ConsoleKey::Escape. Оно возвращает false, когда свойство keyPress.Key
принимает значение ConsoleKey::Escape, что происходит при нажатии клавиши
<ESC>.


Дата добавления: 2015-11-04; просмотров: 26 | Нарушение авторских прав







mybiblioteka.su - 2015-2024 год. (0.037 сек.)







<== предыдущая лекция | следующая лекция ==>