Справочник от Автор24
Поделись лекцией за скидку на Автор24

Понятие об алгоритмах, определение и свойства алгоритма

  • 👀 515 просмотров
  • 📌 482 загрузки
Выбери формат для чтения
Статья: Понятие об алгоритмах, определение и свойства алгоритма
Найди решение своей задачи среди 1 000 000 ответов
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Понятие об алгоритмах, определение и свойства алгоритма» pdf
Тема I. Понятие об алгоритмах 1.1. Определение алгоритма Слово «Алгоритм» происходит от algorithmi – латинского написания имени аль-Хорезми, под которым в средневековой Европе знали величайшего математика из Хорезма (город в современном Узбекистане) Мухаммеда бен Мусу, жившего в 783-850 гг. В своей книге «Об индийском счете» он сформулировал правила записи натуральных чисел с помощью арабских цифр и правила действий над ними столбиком. В дальнейшем алгоритмом стали называть точное предписание, определяющее последовательность действий, обеспечивающую получение требуемого результата из исходных данных. Алгоритм может быть предназначен для выполнения его человеком или автоматическим устройством. Создание алгоритма, пусть даже самого простого, - процесс творческий. Он доступен исключительно живым существам, а долгое время считалось, что только человеку. Другое дело – реализация уже имеющегося алгоритма. Ее можно поручить субъекту или объекту, который не обязан вникать в существо дела, а возможно, и не способен его понять. Такой субъект или объект принято называть формальным исполнителем. Примером формального исполнителя может служить стиральная машинаавтомат, которая неукоснительно исполняет предписанные ей действия, даже если вы забыли положить в нее порошок. Человек тоже может выступать в роли формального исполнителя, но в первую очередь формальными исполнителями являются различные автоматические устройства, и компьютер в том числе. Каждый алгоритм создается в расчете на вполне конкретного исполнителя. Те действия, которые может совершать исполнитель, называются его допустимыми действиями. Совокупность допустимых действий образует систему команд исполнителя. Алгоритм должен содержать только те действия, которые допустимы для данного исполнителя. 4 1.2. Свойства алгоритмов Данное выше определение алгоритма нельзя считать строгим - не вполне ясно, что такое «точное предписание» или «последовательность действий, обеспечивающая получение требуемого результата». Поэтому обычно формулируют несколько общих свойств алгоритмов, позволяющих отличать алгоритмы от других инструкций. Такими свойствами являются:  Дискретность (прерывность, Темаьность) - алгоритм должен представлять процесс решения задачи как последовательное выполнение простых (или ранее определенных) шагов. Каждое действие, предусмотренное алгоритмом, исполняется только после того, как закончилось исполнение  предыдущего.  Определенность - каждое правило алгоритма должно быть четким, однозначным и не оставлять места для произвола. Благодаря этому свойству выполнение алгоритма носит механический характер и не требует никаких   дополнительных указаний или сведений о решаемой задаче.  Результативность (конечность) - алгоритм должен приводить к решению задачи за конечное число шагов.  Массовость - алгоритм решения задачи разрабатывается в общем виде, то есть, он должен быть применим для некоторого класса задач, различающихся только исходными данными. При этом исходные данные могут выбираться из некоторой области, которая называется областью применимо- сти алгоритма. Правила выполнения арифметических операций или геометрических по- строений представляют собой алгоритмы. При этом остается без ответа вопрос, чем же отличается понятие алгоритма от таких понятий, как «метод», «способ», «правило». Можно даже встретить утверждение, что слова «алгоритм», «способ», 5 «правило» выражают одно и то же (т.е. являются синонимами), хотя такое утверждение, очевидно, противоречит “свойствам алгоритма”. Само выражение «свойства алгоритма» не совсем корректно. Свойствами обладают объективно существующие реальности. Можно говорить, например, о свойствах какого-либо вещества. Алгоритм – искусственная конструкция, которую мы сооружаем для достижения определенных целей. Чтобы алгоритм выполнил свое предназначение, его необходимо строить по определенным правилам. Поэтому нужно говорить все же не о свойствах алгоритма, а о правилах построения алгоритма, или о требованиях, предъявляемых к алгоритму. Первое правило – при построении алгоритма, прежде всего, необходимо задать множество объектов, с которыми будет работать алгоритм. Формализованное (закодированное) представление этих объектов носит название данных. Алгоритм приступает к работе с некоторым набором данных, которые называются входными, и в результате своей работы выдает данные, которые называются выходными. Таким образом, алгоритм преобразует входные данные в выходные. Это правило позволяет сразу отделить алгоритмы от “методов” и “способов”. Пока мы не имеем формализованных входных данных, мы не можем построить алгоритм. Второе правило – для работы алгоритма требуется память. В памяти размещаются входные данные, с которыми алгоритм начинает работать, промежуточные данные и выходные данные, которые являются результатом работы алгоритма. Память является дискретной, т.е. состоящей из отдельных ячеек. Поименованная ячейка памяти носит название переменной. В теории алгоритмов размеры памяти не ограничиваются, т. е. считается, что мы можем предоставить алгоритму любой необходимый для работы объем памяти. В школьной «теории алгоритмов» эти два правила не рассматриваются. В то же время практическая работа с алгоритмами (программирование) начинается именно с реализации этих правил. В языках программирования распределение памяти осуществляется декларативными операторами (операторами описания пе6 ременных). При запуске программы транслятор языка анализирует все идентификаторы в тексте программы и отводит память под соответствующие переменные. Третье правило – дискретность. Алгоритм строится из отдельных шагов (действий, операций, команд). Множество шагов, из которых составлен алгоритм, конечно. Четвертое правило – детерминированность. После каждого шага необходимо указывать, какой шаг выполняется следующим, либо давать команду остановки. Пятое правило – сходимость (результативность). Алгоритм должен завершать работу после конечного числа шагов. При этом необходимо указать, что считать результатом работы алгоритма. Итак, алгоритм – неопределяемое понятие теории алгоритмов. Алгоритм каждому определенному набору входных данных ставит в соответствие некоторый набор выходных данных, т.е. вычисляет (реализует) функцию. При рассмотрении конкретных вопросов в теории алгоритмов всегда имеется в виду какая-то конкретная модель алгоритма. 1.3. Виды алгоритмов и их реализация Алгоритм применительно к вычислительной машине – точное предписание, т.е. набор операций и правил их чередования, при помощи которого, начиная с некоторых исходных данных, можно решить любую задачу фиксированного типа. Виды алгоритмов как логико-математических средств отражают указанные компоненты человеческой деятельности и тенденции, а сами алгоритмы в зависимости от цели, начальных условий задачи, путей ее решения, определения действий исполнителя подТемаяются следующим образом: Механические алгоритмы, или иначе детерминированные, жесткие (например, алгоритм работы машины, двигателя и т.п.); Гибкие алгоритмы, например стохастические (вероятностные) и эвристические. 7 Механический алгоритм задает определенные действия, обозначая их в единственной и достоверной последовательности, обеспечивая тем самым однозначный требуемый или искомый результат, если выполняются те условия процесса, задачи, для которых разработан алгоритм. Вероятностный (стохастический) алгоритм дает программу решения задачи несколькими путями или способами, приводящими к вероятному достижению результата. Эвристический алгоритм (от греческого слова “эврика”) – это такой алгоритм, в котором достижение конечного результата программы действий однозначно не предопределено, так как не обозначена вся последовательность действий, не выявлены все действия исполнителя. К эвристическим алгоритмам относят, например, инструкции и предписания. В этих алгоритмах используются универсальные логические процедуры и способы принятия решений, основанные на аналогиях, ассоциациях и прошлом опыте решения сходных задач. Линейный алгоритм – набор команд (указаний), выполняемых последовательно во времени друг за другом. Разветвляющийся алгоритм – алгоритм, содержащий хотя бы одно условие, в результате проверки которого ЭВМ обеспечивает переход на один из двух возможных шагов. Циклический алгоритм – алгоритм, предусматривающий многократное повторение одного и того же действия (одних и тех же операций) над новыми исходными данными. К циклическим алгоритмам сводится большинство методов вычислений, перебора вариантов. Цикл программы – последовательность команд (серия, тело цикла), которая может выполняться многократно (для новых исходных данных) до удовлетворения некоторого условия. Вспомогательный (подчиненный) алгоритм (процедура) – алгоритм, ранее разработанный и целиком используемый при алгоритмизации конкретной задачи. В некоторых случаях при наличии одинаковых последовательностей указаний 8 (команд) для различных данных с целью сокращения записи также выделяют вспомогательный алгоритм. 1.4. Методы изображение алгоритмов На практике наиболее распространены следующие формы представления алгоритмов:    словесная (записи на естественном языке);  графическая (изображения из графических символов);  псевдокоды (полуформализованные описания алгоритмов на условном алгоритмическом языке, включающие в себя как элементы языка программирования, так и фразы естественного языка, общепринятые математиче-  ские обозначения и др.);  программная (тексты на языках программирования). Словесное описание алгоритма Данный способ получил значительно меньшее распространение из-за его многословности и отсутствия наглядности. Рассмотрим пример на алгоритме нахождение максимального из двух значений: Определим форматы переменных X, Y, M, где X и Y – значения для сравнения, M – переменная для хранения максимального значения; получим два значения чисел X и Y для сравнения; сравним X и Y. если X меньше Y, значит большее число Y. Поместим в переменную M значение Y. Если X не меньше (больше) Y, значит большее число X. Поместим в переменную M значение X. Словесный способ не имеет широкого распространения по следующим причинам: 9    такие описания строго не формализуемы;  страдают многословностью записей;  допускают неоднозначность толкования отдельных предписаний. Блок-схема алгоритма А этот способ оказался очень удобным средством изображения алгоритмов и получил широкое распространение в научной и учебной литературе. Структурная (блок-, граф-) схема алгоритма – графическое изображение алгоритма в виде схемы связанных между собой с помощью стрелок (линий перехода) блоков – графических символов, каждый из которых соответствует одному шагу алгоритма. Внутри блока дается описание соответствующего действия. Графическое изображение алгоритма широко используется перед программированием задачи вследствие его наглядности, т.к. зрительное восприятие обычно облегчает процесс написания программы, ее корректировки при возможных ошибках, осмысливание процесса обработки информации. Можно встретить даже такое утверждение: «Внешне алгоритм представляет собой схему – набор прямоугольников и других символов, внутри которых записывается, что вычисляется, что вводится в машину и что выдается на печать и другие средства отображения информации». Здесь форма представления алгоритма смешивается с самим алгоритмом. Принцип программирования «сверху вниз» требует, чтобы блок-схема поэтапно конкретизировалась и каждый блок «расписывался» до элементарных операций. Но такой подход можно осуществить при решении несложных задач. При решении сколько-нибудь серьезной задачи блок-схема «расползется» до такой степени, что ее невозможно будет охватить одним взглядом. Блок-схемы алгоритмов удобно использовать для объяснения работы уже готового алгоритма, при этом в качестве блоков берутся действительно блоки ал- 10 горитма, работа которых не требует пояснений. Блок-схема алгоритма должна служить для упрощения изображения алгоритма, а не для усложнения. Вспомним основные условные обозначения, используемые при графической записи алгоритма (рис 1.1) Начало алгоритма Разветвление 1 Соединитель Ввод (вывод) данных Цикл Операция Ссылка 1 Комментарий Конец алгоритма Рис. 1.1 Псевдокод Псевдокод представляет собой систему обозначений и правил, предназначенную для единообразной записи алгоритмов. Он занимает промежуточное место между естественным и формальным языками. С одной стороны, он близок к обычному естественному языку, поэтому алгоритмы могут на нем записываться и читаться как обычный текст. С другой стороны, в псевдокоде используются некоторые формальные конструкции и математическая символика, что приближает запись алгоритма к общепринятой математической записи. 11 В псевдокоде не приняты строгие синтаксические правила для записи команд, присущие формальным языкам, что облегчает запись алгоритма на стадии его проектирования и дает возможность использовать более широкий набор команд, рассчитанный на абстрактного исполнителя. Однако в псевдокоде обычно имеются некоторые конструкции, присущие формальным языкам, что облегчает переход от записи на псевдокоде к записи алгоритма на формальном языке. В частности, в псевдокоде, так же, как и в формальных языках, есть служебные слова, смысл которых определен раз и навсегда. Они выделяются в печатном тексте жирным шрифтом, а в рукописном тексте подчеркиваются. Единого или формального определения псевдокода не существует, поэтому возможны различные псевдокоды, отличающиеся набором служебных слов и основных (базовых) конструкций. Программное представление алгоритма При записи алгоритма в словесной форме, в виде блок-схемы или на псевдокоде допускается определенный произвол при изображении команд. Вместе с тем такая запись точна настолько, что позволяет человеку понять суть дела и исполнить алгоритм. Однако на практике в качестве исполнителей алгоритмов используются специальные автоматы — компьютеры. Поэтому алгоритм, предназначенный для исполнения на компьютере, должен быть записан на «понятном» ему языке. И здесь на первый план выдвигается необходимость точной записи команд, не оставляющей места для произвольного толкования их исполнителем. Следовательно, язык для записи алгоритмов должен быть формализован. Такой язык принято называть языком программирования, а запись алгоритма на этом языке — программой для компьютера. 12 1.5. Порядок разработки иерархической схемы реализации алгоритмов К основным методам структурного программирования относится, прежде всего, отказ от бессистемного употребления оператора непосредственного перехода и преимущественное использование других структурированных операторов, методы нисходящего проектирования разработки программы, идеи пошаговой детализации и некоторые другие соглашения, касающиеся дисциплины программирования. Всякая программа, в соответствии со структурным подходом к программированию, может быть построена только с использованием трех основных типов блоков. 1. Функциональный блок, который на блок-схеме изображается в виде прямоугольников с одним входом и одним выходом: Функциональному блоку в языках программирования соответствуют операторы ввода и вывода или любой оператор присваивания. В виде функционального блока может быть изображена любая последовательность операторов, выполняющихся один за другим, имеющая один вход и один выход. 2. Условная конструкция. Этот блок включает проверку некоторого логического условия (P), в зависимости от которого выполняется либо один (S1), либо другой (S2) операторы: S1 P S2 13 3. Блок обобщенного цикла. Этот блок обеспечивает многократное повторение выполнения оператора S пока выполнено логическое условие P: P S При конструировании программы с использованием рассмотренных типов блоков эти блоки образуют линейную цепочку так, что выход одного блока подсоединяется ко входу следующего. Таким образом, программа имеет линейную структуру, причем порядок следования блоков соответствует порядку, в котором они выполняются. Такая структура значительно облегчает чтение и понимание программы, а также упрощает доказательство ее правильности. Так как линейная цепочка блоков может быть сведена к одному блоку, то любая программа может, в конечном итоге, рассматриваться как единый функциональный блок с один входом и одним выходом. При проектировании и написании программы нужно выполнить обратное преобразование, то есть этот блок разбить на последовательность подблоков, затем каждый подблок разбить на последовательность более мелких блоков до тех пор, пока не будут получены «атомарные» блоки, рассмотренных выше типов. Такой метод конструирования программы принято называть нисходящим («сверху вниз»). При нисходящем методе конструирования алгоритма и программы первоначально рассматривается вся задача в целом. На каждом последующем этапе задача разбивается на более мелкие подзадачи, каждая подзадача, в конечном итоге на еще более мелкие подзадачи и так до тех пор, пока не будут получены такие подзадачи, которые легко кодируются на выбранном языке программирования. При этом на каждом шаге уточняются все новые и новые детали («пошаговая детализация»). 14 В процессе нисходящего проектирования сохраняется строгая дисциплина программирования, то есть разбиение на подзадачи осуществляется путем применения только рассмотренных типов конструкций (функциональный блок, условная конструкция, обобщенный цикл), поэтому, в конечном итоге, получается хорошо структурированная программа. 2. Классификация алгоритмов По типу используемого вычислительного процесса различают линейные (прямые), разветвляющиеся и циклические алгоритмы. Линейные алгоритмы описывают линейный вычислительный процесс, этапы которого выполняются однократно и последовательно один за другим. Он включает последовательное выполнение следующих этапов:    ввод исходных данных в память ЭВМ;  вычисление искомых величин по формулам;  вывод результатов из памяти ЭВМ на информационный носитель. Пример 1. Составить алгоритм вычисления площади круга по формуле S   R2 . Решение показано на рис. 2.1. Начало Ввод R S  R2 Вывод S Конец Рис. 2.1 15 Разветвляющийся алгоритм описывает вычислительный процесс, реализация которого происходит по одному из нескольких заранее предусмотренных направлений. Направления, по которым может следовать вычислительный процесс, называются ветвями. Выбор конкретной ветви вычисления зависит от результатов проверки выполнения некоторого логического условия. Результатами проверки являются: "истина" (да), если условие выполняется, и "ложь" (нет), при невыполнении условия. Пример 2. Составить алгоритм решения для функции F(x) = 1 при x > 0 и F(x) = 0 при x < 0. Блок - схема разветвляющегося алгоритма показана на рис. 2.2. Циклический алгоритм описывает вычислительный процесс, этапы которого повторяются многократно. Различают простые циклы, не содержащие внутри себя других циклов, и сложные (вложенные), содержащие несколько циклов. В зависимости от ограничения числа повторений выделяют циклы с известным числом повторений и циклы, число повторений которых заранее неизвестно. Начало Ввод х нет да x>0 F=0 F=1 Вывод F Конец Рис. 2.2 16 2.1. Циклы с известным числом повторений При организации этих циклов присутствуют стандартные элементы, сопровождающие любой цикл:  подготовка первого выполнения цикла (присвоение счетчику цикла началь  ного значения);  тела цикла, которое образуют блоки , выполняемые многократно;  изменение значения счетчика циклов и сравнение его с конечным значением. Блок-схемы циклических алгоритмов существенно отличаются структурами повторения "повторять ДО "(повторять до выполнения условия окончания цикла) или "повторять ПОКА " (повторять пока выполняются условия продолжения циклического процесса). В первом варианте проверка условий окончания циклических вычислений осуществляется в конце цикла (рис. 2.3, а), а во втором - в начале цикла (рис. 2.3, б). Как видно из рисунка, цикл "повторять ДО "выполняется, по крайней мере, один раз, а цикл "повторять ПОКА"может сразу привести к выходу из цикла. Подготовка выполнения первого цикла Подготовка выполнения первого цикла Тело цикла Условие окончания нет Подготовка выполнения Тело цикла следующего цикла Подготовка выполнения Условие нет следующего цикла окончания да а б Рис. 2.3 17 да Пример 3. Составить алгоритм решения задачи вычисления N первых членов геометрической прогрессии, используя формулу b n+1 =b n *q для любых b и q, где n - текущий член геометрической прогрессии. Блок-схема алгоритма решения данного примера показана в двух вариантах: с использованием цикла "ДО" (рис. 2.4, а) и цикла "ПОКА"( рис. 2.4, б). Ввод b,q,N Ввод b,q,N n=1 n=1 b=b*q n≤N да Вывод b b=b*q n=n+1 Вывод b нет n>N n=n+1 да Конец Конец а б Рис. 2.4 18 нет 2.2. Циклы с неизвестным числом повторений Примером циклов, число повторений которых не задано, являются итерационные вычислительные процессы. В них решение задачи реализуется путем последовательного приближения к искомому результату. Процесс является циклическим, поскольку заключается в многократных вычислениях. Начальное приближение Y0 выбирается заранее или задается по определенным правилам. Заканчивается итерационное вычисление при выполнении условия Yi -Yi-1 Ymax, то Ymax присваивается значение Y, в противном случае значение Ymax остается неизменным. По завершении цикла на печать выводится максимальное значение функции Ymax. Пример12. Найти наименьший элемент массива x1 ,x 2 , ,x10  и его поряд- ковый номер. Особенностью решения является то, что необходимо найти не только минимальный по значению элемент, но и его порядковый номер. Для этого следует всякий раз, когда в цикле выполняется условие x i Ymax нет x main ( ) { cout << " Это моя первая программа на языке С++"; 32 return 0; } Рис. 4.1 Первая строка данной программы начинается с символа //, показывающего, что следующий за ним текст является комментарием, который компилятор игнорирует. Комментарии вставляются для документирования программы и облегчения ее чтения. Они помогают другим людям читать и понимать вашу программу. Комментарий, начинающийся с символа //, называется однострочным, потому что он должен заканчиваться в конце текущей строки. При использовании многострочных комментариев целесообразно применять символы /* и */. Все, что помещено между ними компилятор игнорирует. Строка # include является директивой препроцессора. Препроцессор - это специальная программа, которая обрабатывает строки программы, начинающиеся со знака #. Данная строка дает указание препроцессору перед компиляцией программы включить в нее информацию, содержащуюся в файле iostream.h. Следом идет обязательная функция main (), а круглые скобки прямо указывают на то, что main - имя функции. Открывающая фигурная скобка отмечает начало последовательности операторов, образующих тело функции. Строка cout << " "; - оператор вывода, с помощью которого выводится на экран дисплея фраза, заключенная в кавычки. Функция может возвращать значение в программу с помощью оператора возврата (return). Этот оператор также означает выход из функции. Если же указанный оператор отсутствует, то функция автоматически возвращает значение типа void (пустой). Закрывающая фигурная скобка отмечает конец последовательности операторов, образующих тело функции. На этой скобке выполнение функции и программы завершается. Программа на С++ состоит из одной или более функций, причем только одна из них обязательно должна называться main(). Функция - это блок программы, который выполняет одно или несколько действий. Описание функции состоит из заголовка и тела (см. рис.4.2). Круглые 33 скобки являются частью имени функции, и ставить их надо обязательно, так как именно они указывают компилятору, что имеется в виду функция, а не просто английское слово main. Фактически каждая функция включает в свое имя круглые скобки, но в большинстве случаев в них содержится некая информация, передаваемая функции. Если же информация не передается, то в фигурных скобках можно указать ключевое слово void (пустой). Перед именем функции указывается ключевое слово, соответствующее типу возвращаемого функцией значения. Если значение не возвращается, то также можно указать ключевое слово void. Заголовок функции void main () { int num; num=1; cout <<”хорошее число=”<< num } Тело функции Рис. 4.2 Заголовок функции состоит из имени функции, а тело функции заключено в фигурные скобки и представляет собой набор операторов, каждый из которых оканчивается символом «;». Оператор описания int num определяет num как переменную целого типа (integer). Любая переменная в языке С++ должна быть описана раньше, чем она будет использована. В С++ используются правила, регулирующие употребление прописных и строчных букв [2]. Команды и стандартные имена функций (т.е. имена функций языка С++) всегда пишутся строчными буквами. Заглавные буквы в языке С++ обычно используются для задания имен констант. В именах своих функций и переменных Вы можете использовать как заглавные, так и строчные буквы. Однако следует помнить, что язык С++ различает использование прописных и строчных букв. Например, если Вы определите в своей программе переменные name, Name, NAME, то для компилятора это три различные переменные. В работе [3] даются следующие рекомендации относительно использования прописных и строчных 34 букв в идентификаторах. Так, в именах переменных целесообразно использовать строчные буквы (нижний регистр), а прописные буквы (верхний регистр) использовать для обозначения констант, макросов и т.д. После того как компьютер заканчивает выполнение инструкций, заданных в вашей программе, программа завершается, и компьютер возвращается в исходное состояние (в то состояние, которое было перед запуском программы). Возврат в исходную среду в случаях, когда функция не возвращает значения, как правило, осуществляется автоматически. Исключение составляют отдельные компиляторы языка С++, которые требуют, чтобы Вы явно указали возврат. Для таких компиляторов вводится инструкция return 0;, которую помещают непосредственно перед фигурной скобкой, завершающей тело функции main(). Если функция возвращает значение, то тело функции должно содержать как минимум один оператор return следующего формата: return выражение ; , где выражение определяет значение, возвращаемое данной функцией. 5. Ввод и вывод в С++ Ввод-вывод в языке С++ осуществляется потоками байтов [4]. Поток - это просто последовательность байтов. В операциях ввода байты пересылаются от устройства ввода (например, клавиатуры, дисковода или соединений сети) в оперативную память. При выводе байты пересылаются из оперативной памяти на устройства (например, экран дисплея, принтер или дисковод). Язык С++ предоставляет возможности для ввода-вывода как на низком, так и на высоком уровнях. Ввод-вывод на низком уровне обычно сводится к тому, что некоторое число байтов данных следует переслать от устройства в память или из памяти в устройство. При такой пересылке каждый байт является самостоятельным элементом данных. Передача на низком уровне позволяет осуществлять пересылку больших по объему потоков ввода-вывода с высокой скоростью, но такая передача обычно оказы35 вается неудобной для программиста и пользователя. Операции ввода-вывода на высоком уровне осуществляются путем преобразования байтов в такие значащие элементы данных, как целые числа, числа с плавающей запятой, символы, строки и т. д. Стандартные библиотеки С++ имеют расширенный набор средств вводавывода, при этом большая часть программ включает заголовочный файл , который содержит основные сведения, необходимые для всех операций с потоками ввода-вывода. Так, например, он включает объекты cin, cout, cerr, clog, которые соответствуют стандартным потокам ввода-вывода и стандартным потокам вывода сообщений об ошибках. Объект стандартного потока ввода cin связан со стандартным устройством ввода, обычно с клавиатурой. Операция взять из потока (cin - the standard input stream - стандартный поток ввода), показанная в приведенном ниже операторе, означает, что величина переменной х должна быть введена из объекта cin в память cin >> x ;. Объект стандартного потока вывода cout связан со стандартным устройством вывода, обычно с экраном дисплея. Операция поместить в поток (cout - standard output stream - стандартный поток вывода), показанная в приведенном ниже операторе, означает, что величина переменной х должна быть выведена из памяти на стандартное устройство вывода cout << x;. Объекты cerr и clog связаны со стандартным устройством вывода сообщений об ошибках. Их различие состоит в том, что при использовании cerr сообщение об ошибках выводится мгновенно, тогда как в случае применения объекта clog сообщения об ошибках помещаются в буфер, где они хранятся до тех пор, пока буфер полностью не заполнится или пока содержимое буфера не будет выведено принудительно. Рассмотрим примеры практической реализации операции ввода-вывода. В программе на рис. 5.1 показан вывод строки, использующий одну операцию поместить в поток. Пример многократного использования операции «поместить в поток» приведен на рис. 5.2. Выполнение этой программы дает те же результаты , что и в примере на рис. 5.1. 36 // Вывод строки # include main( ) { cout << "Добро пожаловать в мир С++ ! \n"; return 0 ; } _________________________________________________ Результаты выполнения программы: Добро пожаловать в мир С++ ! // Вывод строки с помощью двух операций поместить в поток # include main( ) { cout << "Добро пожаловать в" ; cout << " мир С++ ! \n"; return 0 ; } Результаты выполнения программы: Добро пожаловать в мир С++ ! Рис. 5.2 Переход на новую строку в этих программах осуществляется с помощью управляющей последовательности \n. Эту же операцию можно осуществить и с помощью манипулятора потока endl (end line - конец строки), как показано на рис. 5.3. // Вывод строки с использованием манипулятора потока endl # include main( ) { cout << "Добро пожаловать в мир С++ ! "< main( ) { cout << "47 плюс 53 равняется" ; cout << ( 47 + 53 ) ; //выражение cout << endl ; return 0 ; } Результаты выполнения программы: 47 плюс 53 равняется 100 Рис. 5.4 На рис. 5.5 приведена программа, в которой последняя задача решается с помощью одного выражения, использующего способ сцепления операций. // Вывод выражений путем сцепления операций # include main( ) { cout << "47 плюс 53 равняется"<< ( 47 + 53 ) << endl ; return 0 ; } Результаты выполнения программы: 47 плюс 53 равняется 100 Рис. 5.5 На рис.5.6 приведена программа, в которой осуществляется вычисление суммы двух целых чисел, вводимых с клавиатуры при помощи объекта cin и операции взятия из потока >>. // Вычисление суммы двух чисел в режиме диалога # include main( ) { 38 int x, y ; cout << "Введите два целых числа :" ; cin >> x >> y ; cout << "Сумма чисел " << x ,<< "" < main() { int i, j; // объявление переменных float f; i = 10; // присваивание переменным j = 20; // значений f = 99.101 cout << "Вот несколько чисел: "; // операторы вывода cout << i << ' ' << j << ' ' << f; return 0; } // конец функции Результат работы программы: Вот несколько чисел: 10 20 99.101 Рис. 5.7 Здесь в строке cout << i << ' ' << j << ' ' << f; выводится несколько элементов, данных в одном выражении. В общем случае можно использовать единственную инструкцию для вывода любого требуемого количества элементов данных. Обратите внимание, что по мере необходимости следует включить в программу пробе- 39 лы между элементами данных. При их отсутствии выводимые на экран данные будет неудобно читать. На рис. 5.8 приведен пример ввода значения целого числа пользователем. // Ввод данных в режиме диалога #include main() { // начало функции int i; // объявление целой переменной cout << "Введите целое число: "; cin >> i; cout << "Вот Ваше число: " << i << "\n"; return 0; } // конец функции Результат работы программы: Введите число: 100 Вот Ваше число: 100 Рис. 5.8 6. Основные элементы языка С++ Под элементами языка понимают его базовые конструкции, используемые при написании программ [2]. К ним относятся: алфавит, правила записи констант и идентификаторов, основные типы данных и действия над ними. 6.1. Алфавит Алфавитом называют совокупность символов, используемых в языке. В С++ они образуют буквы, цифры и специальные символы. В качестве букв используются прописные буквы латинского алфавита от A до Z и строчные от a до z, а также знак подчеркивания {_}. В качестве десятичных цифр используются арабские цифры от 0 до 9. Специальные символы в языке С++ применяются для 40 различных целей: от организации текста программы до определения указаний компилятору языка С++. Специальные символы перечислены в табл. 6.1. Таблица 6.1 Символ ; Наименование Запятая Точка Точка с запятой Символ < > [ : Двоеточие ] ? Знак вопроса ! ‘ Одиночная кавычка (апостроф) Левая круглая скобка | ) Правая круглая скобка \ { Левая фигурная скобка Правая фигурная скобка ~ Наименование Меньше Больше Левая квадратная скобка Правая квадратная скобка Восклицательный знак Вертикальная черта Наклонная черта вправо (прямой слеш) Наклонная черта влево (обратный слеш) Тильда # Знак номера , . ( } / Символ % & ^ Наименование Процент Амперсант Крышка - Минус = Знак равенства + Плюс * Звездочка “ Двойная кавычка Из символов алфавита формируются лексемы языка [1]:     идентификаторы;  знаки операций;  константы;  Темаители. 6.2. Идентификаторы Идентификаторы используются как имена переменных, функций и типов данных. Они записываются по следующим правилам. 1. Идентификаторы начинаются с буквы (знак подчеркивания также является буквой). 2. Идентификатор может состоять из латинских букв и цифр (пробелы, точки и другие специальные символы при написании идентификаторов недопустимы). 41 3. Между двумя идентификаторами должен быть, по крайней мере, хотя бы один пробел. 4. Идентификатор может быть произвольной длины, но значимыми являются только первые 32 символа в среде WINDOWS и 8 символов в среде DOS; остальные символы игнорируются. Правильные идентификаторы Неправильные идентификаторы $А^** don't HOT-key 1grab wiggly cat HOT_key _grab1 При написании идентификаторов можно использовать как прописные, так и строчные буквы. В отличие от других языков программирования, компилятор языка С++ различает их в записи идентификатора. Для более простого чтения и понимания идентификаторов рекомендуется использовать имена идентификаторов, состоящие из строчных и прописных букв. Каждый идентификатор имеет тип, который устанавливается при его объявлении. Компилятор языка С++ не допускает использование идентификаторов, совпадающих по написанию с ключевыми словами. Например, идентификатор auto недопустим (однако допустим идентификатор AUTO). 6.3. Переменные и константы Данные, обрабатываемые компилятором, - это переменные и константы. Переменные - это данные, которые могут изменять свои значения в процессе выполнения программы. Все переменные в языке С++ должны быть описаны явно. Это означает, что, во-первых, в начале каждой программы или функции Вы должны привести список имен (идентификаторов) всех используемых переменных, а во-вторых, указать тип каждой из них. Оператор описания состоит из спецификации типа и списка имен переменных, Темаенных запятой. В конце обязательно должна стоять точка с запятой. При описании возможно задание начально42 го значения переменной. Имя переменной - любая последовательность прописных и строчных букв английского алфавита, цифр и символа подчеркивания ‘_’. Имя должно начинаться с буквы или символа подчеркивания. Имя может быть произвольной длины, но значимыми являются только первые 32 символа; остальные символы имени игнорируются. Спецификация типа формируется из ключевых слов, указывающих на различные типы данных. Основные типы в C++ подТемаяются на две группы: целочисленные типы и типы с плавающей точкой. К целочисленным типам относятся типы, представленные следующими именами основных типов: char, short, int, long. Имена целочисленных типов могут использоваться в сочетании с парой модификаторов типа signed и unsigned. Эти модификаторы изменяют формат представления данных, но не влияют на размеры выделяемых областей памяти. Модификатор типа signed указывает, что переменная может принимать как положительные, так и отрицательные значения. Модификатор типа unsigned указывает, что переменная принимает только положительные значения. Основные характеристики целочисленных типов выглядят следующим образом (табл. 6.2): Таблица 6.2 Тип данных signed char unsigned char signed short unsigned short signed int unsigned int signed long unsigned long Байты 1 Биты 8 1 2 8 16 2 2 2 4 4 16 16 16 32 32 Min - 128 127 Max 255 -32768 -32768 -2147483648 32767 65535 32767 65535 2147483647 4294967295 По умолчанию считается, что данные типов char, int, short int, long используются со знаком. Поэтому ключевое слово signed можно не указывать. Данные типа char используются для хранения символов. Под символом подразумевается одиночная буква, цифра или знак, занимающий только один байт памяти. Переменные типа char могут использоваться как данные со знаком (signed char) и как данные без знака (unsigned char). Если тип char рассматривается как signed, то 43 старший байт его кода определяет знак. В этом случае диапазон значений типа char - от -128 до + 127. В случае unsigned char все восемь бит рассматриваются как код, а диапазон возможных значений - от 0 до 255. К плавающим типам относятся три типа, представленные следующими именами типов, модификаторов и их сочетаний: float, double, long double. Они используются для работы с вещественными числами, которые представляются в форме записи с десятичной точкой и в "научной нотации". Разница между нотациями становится очевидной из простого примера, который демонстрирует запись одного и того же вещественного числа в различных нотациях. 297.7 2.977E2 и ещё один пример... 0.002355 2.355E-3 В научной нотации слева от символа E записывается мантисса, справа - значение экспоненты, которая всегда равняется показателю степени 10. Ниже представлены основные характеристики типов данных с плавающей точкой (табл. 6.3): Таблица 6.3 Тип данных float double long double Байты 4 8 10 Биты Min 32 3.4E-38 64 1.7E-308 80 3.4E-4932 Max 3.4E+38 1.7E+308 3.4E+4932 Константы - это данные, которые устанавливаются равными определенным значениям еще до выполнения программы и сохраняют их на протяжении выполнения всей программы. Имеется 4 типа констант: целые, с плавающей запятой, символьные и перечисляемые. Например, 25 и -5 - целые константы, 4.8 , 5E15, 5.1E8 - константы с плавающей запятой, ‘А’, ‘\0’, ‘\n’, ‘007’ - символьные константы. Набор символов внутри двойных кавычек: "Это строка "- есть строковая константа. Целые константы могут быть десятичные, восьмеричные и шестнадцатеричные. 44 Десятичные константы могут принимать значения от 0 до 4 294 967 295. Константы, выходящие за указанные пределы, вызывают ошибку. Отрицательные десятичные константы - это просто константы без знака, к которым применена унарная операция минус. Восьмеричные константы начинаются с символа нуля, после которого сле- дуют восьмеричные цифры (от 0 до 7), например 032. Если восьмеричная константа содержит недопустимые цифры 8 или 9, выдается сообщение об ошибке. Ошибка будет также выдаваться при превышении восьмеричной константой значения 037777777777. Шестнадцатеричные константы начинаются с 0x (или 0X). Значения шест- надцатеричных констант, превышающие 0xFFFFFFFF, приводят к ошибке. Символьная константа - это один или более символов, заключенных в оди- нарные кавычки, например 'F', '=', '\n'. В C++ константа из одного символа имеет тип char. Для введения управляющих последовательностей, позволяющих получить визуальное представление некоторых, не имеющих графического аналога символов, используется группа символьных констант, которые начинаются со специального символа обратной косой черты ('\'). В таблице 6.4 показаны допустимые управляющие последовательности. Строковые константы образуют специальную категорию констант, используемых для работы с фиксированными последовательностями символов. Строковая константа имеет тип данных array of char и записывается как последовательность произвольного количества символов, заключенных в двойные кавычки: "Это строковая константа!". Нулевая (пустая) строка записывается как "". Перечисляемые константы представляют собой идентификаторы, определенные в объявлениях типа enum. Эти идентификаторы обычно выбираются как мнемонические обозначения для удобства обращения с данными. Перечисляемые константы имеют целочисленный тип данных. Они могут быть использованы в любых выражениях, в которых допустим целочисленный тип данных. Используе45 мые идентификаторы должны быть уникальными в пределах контекста объявления enum. Значения, принимаемые этими константами, зависят от формата объявления перечислимого типа и присутствия опциональных инициализаторов. Например, в операторе enum color { red, yellow, green }; объявляется переменная с именем color, которая может принимать константные значения red, yellow или green. Таблица 6.4 Последовательность символов Обозначение в ASCII-таблице \a BEL \b BS \f FF \n LF CR \r \t \v \\ \' \" \? \0oo \xhh HT VT \ ‘ “ ? Выполняемое действие При выводе на экран вызывает звуковой сигнал При выводе на принтер и на экран вызывает сдвиг текущей позиции влево Перевод формы. При выводе на принтер вызывает прогон бумаги до начала следующей страницы При выводе на принтер и на экран переводит строку При выводе на принтер и на экран перемещает текущую позицию на начало строки, не переводит строку Горизонтальная табуляция Вертикальная табуляция Символ обратной косой черты Символ одиночной кавычки Символ двойной кавычки Знак вопроса Последовательность цифр, начинающаяся с 0, трактуется как код внутреннего представления, заданный в восьмеричной системе счисления числом оо Последовательность цифр, начинающаяся с x , трактуется как код внутреннего представления, заданный в шестнадцатеричной системе счисления числом hh 6.4. Определение констант с помощью директивы препроцессора #define Константы в языке С++ можно задавать либо в явном виде (т.е. указывать непосредственно значение константы), либо использовать идентификатор, которому присваивается значение константы. Определение константы с помощью идентификатора осуществляется в заголовке программы по следующей форме [2,4]: 46 #define имя строка, где имя - идентификатор; строка - любая последовательность символов, отделяемая от имени хотя бы одним пробелом и заканчиваемая в текущей строке. Директива #define выполняет простую текстовую подстановку, т.е. когда препроцессор встречает имя, он заменяет его на строку. Примеры: #define I 5 // ставит в соответствие имени I число 5 #define J 4 #define PI 3.1415 Необходимо обратить внимание на то, что при использовании директивы define тип константы не имеет значения (константы I, J, PI не имеют никакого конкретного типа). Определение констант с помощью директивы define наиболее предпочтительно, так как в случае изменения их значений в программе понадобится внести изменения только в одном месте. 7. Операции и выражения 7.1. Выражение и его интерпретация Выражение в языке С++ (С) - это последовательность операндов, операций и символов-Темаителей [2]. Операнды - это переменные, константы либо другие выражения. Темаителями в С++ являются символы [ ] ( ) { } , ; : . * = #, каждый из которых выполняет свою функцию. Выражение может состоять из одной или более операций и определять выполнение целого ряда элементарных шагов по преобразованию информации. Компилятор соблюдает строгий порядок интерпретации выражений, называемый правилами предшествования. Этот порядок может быть изменен, если отдельные части выражения заключить в круглые скобки. Элементарная операция по преобразованию информации задается знаком операции. По числу операндов, участвующих в операции, различают следующие типы:  унарные (имеющие один операнд); 47  бинарные (имеющие два операнда);   тернарные (имеющие три операнда). По типу выполняемой операции различают:  арифметические операции;   логические операции и операции отношения;   операцию условия;   операцию присваивания;   операцию sizeof;   операцию преобразования типов. 7.2. Арифметические операции Язык С++ (С) включает [3]:  арифметические операции сложения (задается знаком +), вычитания и унарный минус (-), умножения (*), деления (/), операцию определения  остатка (%);  операции инкремента (++) и декремента (--). Операции сложения, вычитания, умножения и деления являются стандарт- ными и выполняются так же, как и в большинстве других алгоритмических языков. Операция определения остатка (%) (или деления по модулю) служит для определения остатка от деления числа целого типа, стоящего слева от знака, на целое число, расположенное справа от знака. Ее поясняет следующий пример: int x =7, y = 2, z ; z = x % y; // z=1 - остаток от целого деления 7/3 z = x/y; // z =2 - результат деления целых чисел Операции инкремента и декремента соответственно увеличивают и уменьшают операнд на 1. Операции ++ и -- могут применяться только к переменным. 48 Используются две формы их записи: префиксная (знак операции располагается слева от операнда) и постфиксная (знак операции справа от операнда). В префиксной форме сначала выполняется увеличение операнда на 1, и увеличенное значение используется в выражении. В постфиксной форме сначала берется значение операнда, и только после этого его значение увеличивается на 1. Например: int x=0, y = 1, z=0; z = x++; // в результате z = 0, x = 1 z = ++x; // z=2, x =2 z = ++y; // z=2, y= 2 Ниже приведен пример программы, иллюстрирующей работу префиксной и постфиксной форм инкремента. # include < iostream .h> main{} { int x; x=5; cout << x <<"\n" ; cout << x++ <<"\n" ; // Постфиксная форма инкремента cout << x <<"\n" ; x=5; cout << x <<"\n" ; cout << ++ x <<"\n" ; // Префиксная форма инкремента cout << x <<"\n" ; return 0; }---------------------------------------------------------------- Результаты работы программы: 55 65 66 Форма записи операций ++ и -- сказывается в составных выражениях. Например, три оператора присваивания вида number = number + 1; sum = sum + 1; x = x + 1; 49 могут быть записаны более кратко number += 1; sum + = 1; x += 1; , а с использованием операций инкремента в префиксной форме: ++number; ++sum ; ++x; или в постфиксной форме: number++; sum++; x ++; 7.3. Логические операции и операции отношения Логические операции и операции отношения используются при формировании логических выражений, имеющих только два значения: 1, если логическое выражение ИСТИННО; 0, если логическое выражение ЛОЖНО. Логические выражения наиболее часто используют вместе с операторами управления потоком вычислений - операторами циклов и ветвлений [2]. С++ поддерживает следующие логические операции: && - логическое И; дает результат 1 (ИСТИНА), если все операнды имеют значение 1 (ИСТИНА); в противном случае дает значение 0 (ЛОЖЬ); || - логическое ИЛИ; дает результат 1 (ИСТИНА), если хотя бы один из операндов имеет значение 1 (ИСТИНА); в противном случае дает значение 0 (ЛОЖЬ); ! - логическое НЕ; дает результат 1 (ИСТИНА), если операнд справа от знака имеет значение 0 (ЛОЖЬ); в противном случае дает значение 1 (ИСТИНА). Возможные результаты выполнения логических операций представлены в табл.7.1. 50 Таблица 7.1 а 1 1 b 1 1 a&&b 1 ab 1 1 1 !a 1 1 Операнды логических выражений вычисляются слева направо. Если значения первого операнда достаточно, чтобы определить результат операции, то второй операнд не вычисляется. Логические операции не вызывают стандартных арифметических преобразований. Они оценивают каждый операнд с точки зрения его эквивалентности нулю. Результатом логической операции является 0 или 1, тип результата int. С++ поддерживает следующие операции отношения: ● > - больше; дает результат 1, если операнд слева от знака больше операнда справа; в противном случае дает 0;  < - меньше; дает результат 1, если операнд слева меньше операнда     справа; в противном случае дает 0;  = = - равно; дает результат 1, если операнд слева от знака равен операнду справа; в противном случае дает 0;  >= - больше или равно; дает результат 1, если операнд слева от знака больше или равен операнду справа; в противном случае дает 0;  <= - меньше или равно; дает результат 1, если операнд слева от знака меньше или равен операнду справа; в противном случае дает 0;  != - не равно; дает результат 1, если операнд слева от знака не равен операнду справа; в противном случае дает значение 0. Приведем пример программы, получающей со стандартного ввода два зна- чения и выводящей на стандартный вывод результат выполнения логических операций и операций отношения: #include #include 51 void main( ) { clrscr(); float p1,p2; cout << "Введите первое значение p1 = "; cin >> p1; cout << "Введите второе значение p2 = "; cin >> p2; cout << " p1 > p2 дает " << (p1 > p2) << "\n"; cout << " p1 < p2 дает " << (p1 = p2 дает " << (p1>=p2) << "\n"; cout << " p1 <= p2 дает " << (p1<=p2) << "\n"; cout << " p1 != p2 дает " << (p1!=p2) << "\n"; cout << " !p1 дает " << !p1 << "\n"; cout << " !p2 дает " << !p2 << "\n"; cout << " p1 || p2 дает " << (p1||p2) << "\n"; cout << " p1 && p2 дает " << (p1&&p2) << "\n"; cout<<"\n -------------------------------- \n"; cout«м\nНажмите любую клавишу getch(); }__________________________________________________ Результаты работы программы: Введите первое значение p1 = 2 Введите второе значение p2 = 3 p1 > p2 дает 0 p1 < p2 дает 1 p1 == p2 дает 0 p1 >= p2 дает 0 p1 <= p2 дает 1 p1 != p2 дает 1 дает 0 !p1 дает 0 !p2 p1 || p2 дает 1 p1 && p2 дает 1 7.4. Операция условия В языке C++ (С) имеется одна тернарная операция - условная операция, которая имеет следующий формат: операнд-1 ? операнд-2 : операнд-3. 52 Операнд-1 должен быть целого или плавающего типа или быть указателем. Он оценивается с точки зрения его эквивалентности 0. Если операнд-1 не равен 0, то вычисляется операнд-2, и его значение является результатом операции. Если операнд-1 равен 0, то вычисляется операнд-3, и его значение является результатом операции. Следует отметить, что вычисляется либо операнд-2, либо операнд3, но не оба. Пример: max = (d<=b) ? b : d; Переменной max присваивается максимальное значение из переменных d и b. 7.5. Операция присваивания Язык С++ имеет несколько особенностей выполнения операции присваивания. Помимо простого присваивания посредством операции "=" , С++ поддерживает составные операции присваивания, которые перед присваиванием выполняют дополнительные операции над своими операндами [1]. Формат операции простого присваивания (=): операнд 1 = операнд2; . Сначала вычисляется выражение, стоящее в правой части операции, а потом его результат записывается в область памяти, указанную в левой части. Например: int a=3,b=5,c=7; // Результаты работы фрагмента программы: a=b;b=a;c=c+1; // a=5;b=5;c=8. Операция составного присваивания состоит из простой операции присваивания, скомбинированной с другой бинарной операцией. В составном присваивании вначале выполняется операция, специфицированная аддитивным оператором, а затем результат присваивается левому операнду. Выражение составного присваивания, например, имеет вид операнд 1> += < операнд 2> и может быть понято как операнд 1> = < операнд 1> + < операнд 2> 53 Однако выражение составного присваивания неэквивалентно расширенной версии, поскольку в выражении составного присваивания < операнд 1> вычисляется только один раз, в то время как в расширенной версии оно вычисляется дважды: в операции сложения и в операции присваивания. В общем случае формат операции составного присваивания можно представить так: < операнд1> ( бинарная операция) = < операнд2>, где (бинарная операция) - одна из операций, задаваемых знаками *, /, +, -,%. Результатом операции составного присваивания являются значение и тип левого операнда. Например: a += b; a -= b; a *= b; a /= b; a %= b; // a= a + b; // a= a - b; // a= a * b; // a= a / b; // a= a % b; 7.6. Операция sizeof С помощью операции sizeof можно определить размер памяти, которая соответствует идентификатору или типу. Она имеет следующий формат: sizeof (выражение) . В качестве выражения может быть использован любой идентификатор (кроме имени функции), либо имя типа, заключенное в скобки. Если в качестве выражения указано имя массива, то результатом является размер всего массива (т. е. произведение числа элементов на длину типа). Результатом операции sizeof является размер в байтах типа или объявленной переменной, а применительно к массивам операция возвращает число байтов, необходимое для размещения всех элементов массива. Если определяется размер переменной, то имя переменной можно также указывать через пробел после sizeof без круглых скобок. 54 Пример: #include void main() { int i; char c; long double ff; cout << " Данные типа char занимают " << sizeof(char) << "байт \n"; cout << " Данные типа int занимают " << sizeof(int) << "байт \n"; cout << " Данные типа long занимают " << sizeof(long) << "байт \n"; cout << " Переменная i занимает " << sizeof i << "байт \n"; cout << " Переменная c занимает " << sizeof c << "байт \n"; cout << " Переменная ff занимает " << sizeof ff << "байт \n"; } В результате выполнения программы на экран будет выведено: Данные типа char занимают 1 байт Данные типа int занимают 2 байт Данные типа long занимают 4 байт Переменная i занимает 2 байт Переменная c занимает 1 байт Переменная ff занимает 10 байт 7.7. Преобразование типов Операнды бинарной операции могут быть разного типа. В этом случае перед ее выполнением компилятор предварительно приводит операнды к одному типу. Преобразование типов выполняется по следующим правилам [2]: 1. операнды разных типов приводятся к "старшему", т.е. более длинному типу. Ниже перечислены типы в порядке убывания старшинства: самый старший - long double, double, float, unsigned long, long int, unsigned int, char - самый младший; 2. при выполнении операции присваивания результат приводится к типу переменной слева от знака операции. Явное приведение типа операндов выполняется с помощью операции type(expression), где type - любой допустимый тип языка С++. Результатом опера- 55 ции type (expression) является значение выражения expression, преобразованное к типу type. Пример: --- int x; float y=1.6; … x = int (5.2) + int (y); --- В результате выполнения данного фрагмента программы получим x=6. Преобразование младшего (меньшего по размеру типа) к старшему происходит корректно, а старшего к младшему может вызвать затруднения (все число может не поместиться в выделенной под него области памяти, и тогда результат операции будет не верный). 7.8. Порядок выполнения операций Все операции, распознаваемые компилятором, упорядочены по приоритету. Выражения с более приоритетными операциями вычисляются первыми. Порядок вычислений может изменяться круглыми скобками. Если внутри скобок или при отсутствии скобок вообще в выражении есть несколько операций одного приоритета, компилятор учитывает дополнительно порядок выполнения операций, например, слева направо или справа налево. Заключенное в скобки выражение интерпретируется компилятором в первую очередь. В сложном выражении первым выполняется выражение в самых «глубоких» скобках. В табл.7.2 приведен перечень всех операций языка, упорядоченных в порядке убывания приоритета (тонкая черта отделяет операции с одинаковым приоритетом). Таблица 7.2 Символ операции Обращение к функции Выделение элемента массива Выделение поля структурной переменной Выделение поля структурной переменной по указателю на ее начало () [] . Порядок выполнения Слева направо То же - “- -> - “- Логическое отрицание Поразрядное логическое НЕ Изменение знака (унарный минус) Инкремент ! ~ ++ Справа налево То же - “- “- Название 56 Декремент Определение адреса переменной Обращение к памяти по значению указателя Преобразование к типу Определение размера в байтах -& * (type) sizeof */ % Умножение Деление Определение остатка целого деления Сложение Вычитание Сдвиг влево Сдвиг вправо Меньше Меньше или равно Больше Больше или равно Равно Не равно Поразрядное логическое И Поразрядное исключающее ИЛИ (XOR) Поразрядное логическое ИЛИ Логическое И Логическое ИЛИ Операция условия Присваивание + << >> <> >= != & ^ | && || ?: = += -= *= /= %= <<= >>= != ^= &= , Операция «запятая» - “- “- “- “- “- “Слева направо То же - “Слева направо То же Слева направо То же Слева направо То же - “- “Слева направо То же Слева направо Слева направо Слева направо Слева направо Слева направо Справа налево Слева направо То же - “- “Слева направо 8. Операторы управления 8.1. Общие сведения Операторы управления вычислительным процессом позволяют выполнять ветвление, циклическое повторение одного или нескольких операторов, передачу управления в нужное место кода программы. Под вычислительным процессом понимают процесс выполнения операторов программы. Операторы программы могут быть простыми или составными. Простой оператор - это оператор, не содержащий другие операторы. Темаителем простых операторов служит точка с запятой. Специальным случаем простого оператора является пустой оператор, состоящий из единственного символа ';'. Составной оператор, или блок, - это любая совокупность простых операторов, заключенная в фигурные скобки {}. Составной 57 оператор идентичен простому оператору и может находиться в любом месте программы, где синтаксис языка допускает наличие оператора. Все операторы языка могут быть условно Темаены на следующие категории:  условные операторы, к которым относятся оператор условия if и опе   ратор выбора switch;  операторы цикла (for, while, do while);  операторы перехода (break, continue, return, goto);  другие операторы (оператор "выражение", пустой оператор). 8.2. Оператор if Оператор условия if выбирает в программе из группы альтернатив возмож- ное продолжение вычислительного процесса. Выбор выполняется, исходя из значения заданного логического выражения. Оператор if имеет следующую общую форму записи: if (логическое выражение) оператор A; [else оператор B;] При выполнении оператора if сначала вычисляется логическое выражение. При результате ИСТИНА (любое отличное от нуля значение) выполняется оператор A, в противном случае (результат логического выражения ЛОЖЬ (равен 0)) выполняется оператор B. Если ключевое слово else отсутствует, а результат логического выражения ЛОЖЬ, то в этом случае оператор A пропускается, а управление передается на следующий после if оператор. Если в какой-либо ветви требуется выполнить несколько операторов, их необходимо заключать в блок. Блок может содержать любые операторы, в том числе и другие условные операторы, образуя так называемые вложенные if [1]. Примеры: if (a<0) b=1; if (a<0 &&(a > d || a = = 0)) b++; else {b*=a;a=0;} if (aa) max = b; else max = a; 58 //1 //2 //3 //4 //5 В примере 1 отсутствует ветвь else. В примере 2 показано, что при необходимости проверки нескольких условий их объединяют знаками логических операций. Оператор в примере 3 вычисляет наименьшее значение из трех переменных. В примере 4 показано, что в логическом выражении могут использоваться не только операции отношения, а в примере 5 показано, как с помощью оператора if можно найти максимальное из двух заданных чисел. При использовании вложенных циклов может возникнуть неоднозначность в понимании того, к какой из вложенных конструкций if относится элемент else. Например, в конструкции if (условие 1 ) if (условие 2) оператор 1 ; else оператор 2; элемент else будет отнесен компилятором ко второй конструкции if , т.е. оператор 2 будет выполняться в случае, если первое условие истинно, а второе ложно. Иначе говоря, конструкция будет прочитана как if (условие 1 ) { if(условие 2) оператор 1; else оператор 2; } Если же необходимо отнести else к первому оператору if, то с помощью соответствующих установок фигурных скобок фрагмент программы запишется в виде if (условие 1 ) { if(условие 2) оператор 1; } else оператор 2; Рассмотрим еще один пример программы: #include void main() { int a=1, b=10; 59 if (a ==1) if (b == 1) cout << " \n a=1, b = 1\n"; else cout << "\n a не равно 1\n"; } В результате выполнения этой программы на экран будет выводиться строка "a не равно 1", хотя на самом деле a=1. Ошибка происходит потому, что компилятор сопоставляет if с ближайшим else (т.е. со 2-м if). Ошибку можно исправить, поставив фигурные скобки, ограничивающие блок. В результате данный пример будет иметь вид #include void main( ) { int a=1, b=10; if (a ==1) { if (b == 1) cout << " \n a=1, b = 1\n"; } else cout << "\n a не равно 1 \n"; cout << "Конец"; } Рассмотрим еще один пример: int main ( ) { int t=2, b=7, r=3; if (t>b) { if (b < r) r=b; } else r=t; return (0); } 60 В результате выполнения этой программы r станет равным 2. Если же в программе опустить фигурные скобки, стоящие после оператора if, то программа будет иметь следующий вид: int main ( ) { int t=2, b=7, r=3; if ( a>b ) if ( b < c ) t=b; else r=t; return (0); } В этом случае r получит значение, равное 3, так как ключевое слово else относится ко второму оператору if, который не выполняется, поскольку не выполняется условие, проверяемое в первом операторе if. При выборе результата из нескольких возможных вариантов широко используется следующая конструкция оператора if, часто определяемая как else - if : if (логическое выражение 1) оператор 1; else if (логическое выражение 2) оператор 2; else if (логическое выражение 3) оператор 3; …………………………………………………….. else if (логическое выражение N) оператор N; else оператор; В этой конструкции все логические выражения просматриваются последовательно. Если какое-то выражение оказывается истинным, то выполняется относящийся к нему оператор, и этим вся цепочка заканчивается. Каждый оператор может быть либо отдельным оператором, либо группой операторов в фигурных скобках. Последняя часть с else реализуется, когда ни одно из проверяемых условий не выполняется. Иногда по условиям задачи этот оператор может быть опущен. 61 Для иллюстрации выбора из четырех возможных вариантов приведем программу, определяющую номер квадранта в декартовой системе координат для точки, значения координат которой заранее не известны, а задаются в режиме диалога. #include #include #include #include main() { float x=0, y=0; int n; // номер квадранта clrscr(); // очистка экрана cout<<"\n Найти квадрант для точки с заранее неизвестными координатами\n"; cout<<"\nВведите координату X="; cin>> x; cout<<"\nВведите координату Y="; cin>> y; if( x > 0 && y>0 ) n = 1; else if (x < 0 && y > 0) n = 2; else if (x < 0 && y < 0) n = 3; else n = 4; cout<<"\n\nТочка находится в "< #include void main() { clrscr(); char x; cout << "Введите первую букву имени функции\n"; cout << "S- Sin\nC - Cos\nA - Atan\n"; cin >> x; switch (x) { case 's': case 'S': cout << "Вычисление синуса аргумента в радианахn"; break; case 'c': case 'C': cout << "Вычисление косинуса аргумента в радианахn"; break; case 'a': case 'A': cout << " Вычисление арктангенса аргумента "; cout << " в радианахn"; break; 63 default: cout << "Ошибка\n"; } cout<<"\n -------------------------------- \n"; cout«"\nНажмите любую клавишу…" getch(); } ________________________________________________________ Результаты работы программы: Введите первую букву имени функции S- Sin C - Cos A - Atan C Вычисление косинуса аргумента в радианах Рис. 8.1 Программа на рис. 8.2 использует оператор switch для подсчета числа различных оценок, полученных студентами на экзаменах. //Использование оператора switch для подсчета числа оценок # include #include main() { int i = 1 , grade; // оценка int otl = 0, xor = 0, udov = 0, neudov=0; while (i<=8){ cout << "Введите значение оценки ="; cin>>grade; switch (grade) { case 5: ++ otl ; break; case 4: ++xor; break; case 3: ++ udov; break; case 2: ++ neudov; break; default: 64 cout << "\nВведена неправильная оценка\n" ; break; } ++i ; } Cout<<кол.отл.оценок=< #include #include #include main() { clrscr(); int number=2; while(number<=1000) number=2*number ; cout<<\nчисло="< #include void main( ) { clrscr( ); int n=2, sum=0, i=1; while (n <= 10) { sum += n; // sum = sum + n; cout << "В"<<" "< #include #include #include main() { 67 clrscr(); int i; float x,y,Xmax; cout<<"Введите максимальное значение переменной Xmax ="; cin >> Xmax; x=0; while(x<=Xmax) { y=sin(x); x+=0.5; cout<<"\ny="< #include void main() { clrscr(); int n=1,sum=0; do { sum += n; // sum = sum +n; cout << "Сейчас n = " << n << "\t sum = " << sum << "\n"; n += 2; } while (n <= 10); cout << "\nОкончательный результат: \n"; cout << " n = " << n << "\t sum = " << sum << "\n"; cout<<"\n\n"; cout«"\nНажмите любую клавишу…"; getch(); }_____________________________________________________ Результаты работы программы: Сейчас n = 1 sum = 1 Сейчас n = 3 sum = 4 Сейчас n = 5 sum = 9 Сейчас n = 7 sum = 16 Сейчас n = 9 sum = 25 Окончательный результат: Сейчас n = 11 sum = 25 Рис. 8.6 8.5. Оператор for Оператор for повторяет блок рабочих операторов указанное число раз и имеет следующую структуру: for (имя переменной = начальное значение; конечное значение; приращение) оператор, где  имя переменной - имя управляющей переменной, используемой как счетчик цикла; 69     начальное значение - начальное значение управляющей переменной;  конечное значение - конечное значение управляющей переменной;  приращение - шаг изменения значения управляющей переменной;  оператор - один или несколько рабочих операторов, образующих тело цикла. Если тело цикла содержит более одного оператора, последние должны за- ключаться в фигурные скобки. Способы изменения управляющей переменной в структуре оператора for покажем на следующих примерах: 1. Изменение управляющей переменной от 1 до 100 с шагом 1 for (int i = 1; i<=100); 2. Изменение управляющей переменной от 100 до 1 с шагом -1 for (int i = 100; i >=1; i--); 3.Изменение управляющей переменной от 7 до 77 с шагом 7 for (int i = 7; i <= 77; i += 7); 4. Изменение управляющей переменной от 20 до 2 с шагом -2 for (int i = 20; i >= 2; i -= 2); 5.Изменение управляющей переменной от 7 до 77 с шагом 7 for (int i = 7; i <= 77; i += 7); Следующие два примера содержат простые приложения структуры for. Первая программа использует структуру for для суммирования всех четных чисел от 2 до 100. //Суммирование с помощью for # include #include main ( ) { int sum = 0; for (int i = 2; i <= 100; i +=2) sum += i; cout << "Сумма равна" << sum<<"\n"; return 0; } Здесь первое выражение в структуре for вводит целую переменную i, являющуюся счетчиком циклов, и инициализирует ее значение 2. Второе выражение 70 проверяет условие завершения цикла. В данном случае цикл завершается, когда переменная i примет значение >100. Третье выражение структуры for увеличивает значение i на 2 после каждого выполнения цикла. Следующий пример вычисляет с помощью структуры for значения функции y = x при изменении x от -3 до 3 с шагом 0,5. #include #include #include #include main() { clrscr(); float x,y; for ( x = -3 ; 3; x += 0.5) { y = x*x; cout<<"\ny="< #include void main( ) { clrscr(); int y=0,n,m; 71 for (n = 1; n <= 2; n++) for ( m = 1; m <= 2; m++) { y = y + (n*n + m*m); cout << " n = " << n << "\t"; cout << "m = " << m << "\ty = " << y << "\n"; } cout << "\nОтвет: y = " << y; cout<<"\n\n"; cout << "\nНажмите любую клавишу…"; getch(); } _______________________________________________________ Результаты работы программы: n=1 m=1 y=2 n=1 m=2 y=7 n=2 m=1 y=12 n=2 m=2 y=20 Ответ: y =20 Рис. 8.7 В этой программе внутренний цикл for содержит несколько операторов, объединенных в один блок с помощью фигурных скобок. 8.6. Операторы break и continue Эти операторы изменяют поток управления. Когда оператор break выполняется в структурах while, for, do/while или switch, происходит немедленный выход из структуры. Программа продолжает выполнение с первого оператора после структуры. Обычное назначение оператора break - досрочно прерывать цикл или пропустить оставшуюся часть структуры switch, например: // Применение оператора break в структуре for #include main ( ) { for (int x=1; x<=10; x++) { if (x == 5) break; cout << "x=" << x; 72 } cout << "\n ЦИКЛ прерван при х== " < main() { for ( int x = 1; x <= 10; x++) { if (x == 5) continue; // пропуск оставшейся части цикла // только при х == 5 cout <<" x " < (Список параметров) { Операторы тела функции } Тип определяет тип значения, которое возвращает функция с помощью опе- ратора return. Если тип не указан, то по умолчанию предполагается, что функция возвращает целое значение (типа int). Список параметров состоит из перечня типов и имен параметров, Темаен- ных запятыми. Функция может не иметь параметров, но круглые скобки необходимы в любом случае. Первая строка описания функции, содержащая тип возвращаемого значения, имя функции и список параметров, называется заголовком функции. Параметры, перечисленные в заголовке описания функции, называются формальными, а записанные в операторе вызова функции - фактическими. Тип возвращаемого значения может быть любым, кроме массива и функции. В приведенном ниже фрагменте программы функция перемножает два числа и возвращает результат в основную программу с помощью оператора return через переменную z. int multiply(int x, int y) // заголовок функции { int z = (x * y ); // тело функции return z ; } До использования функции ее необходимо объявить. Объявление функции осуществляется с помощью прототипа, который сообщает компилятору, сколько аргументов принимает функция, тип каждого аргумента и тип возвращаемого значения. Пример использования функции для вычисления произведения двух чисел показан на рис. 9.1. // Вычисление произведения двух чисел #include #include #include 74 int multiply(int x, int y); // объявление прототипа функции main( ) { clrscr(); int x, y, result; cout <<"\nВведите первое число:"; cin >> x; cout <<"\nВведите второе число:"; cin>>y; result = multiply(x, y); // вызов функции cout <<"\nРезультат =" << result; cout <<"\nНажмите любую клавишу getch(); return 0 ; } int multiply(int x, int y) //заголовок функции { return x * y ; // тело функции }_______________________________________________________ Результаты работы программы: Введите первое число: 3 Введите второе число: 2 Результат = 6 Рис. 9.1. Окончание Эта программа запрашивает у пользователя два числа, а затем вызывает функцию multiply() для вычисления произведения. Прототип функции объявляется в голове программы и представляет собой тот же заголовок функции, но с точкой запятой “ ; ” в конце. При желании имена переменных в прототипах можно не указывать. Пример подобной программы показан на рис. 9.2. // Нахождение максимального числа из трех заданных чисел #include #include int maxs(int, int, int); // прототип функции main() { clrscr(); //очистка экрана int x, y, z, s; 75 cout <<"\nВведите три числа:\n"; cin>>x>>y>>z; s=maxs(x, y, z) ; cout<<"\nРезультат ="<< s; cout<<"\nНажмите любую клавишу…"; getch(); return 0; } int maxs(int x, int y, int z) { int max=x; if(y>max) max=y; if(z>max) max=z; return max; } ____________________________________________________________ Результаты работы программы: Введи- те три числа: 5 15 1 Результат =15 Рис. 9.2 Прототип функции maxs имеет вид int maxs(int, int, int);. Этот прототип указывает, что maxs имеет три аргумента типа int и возвращает результат типа int. Отметим, что прототип функции имеет такой же вид, что и заголовок описания функции maxs, за исключением того, что в него не включены имена параметров (x,y,z). Значение возвращается из функции с помощью оператора return. Тип возвращаемого значения может быть любым, кроме массива и функции. Могут быть также функции, не возвращающие никакого значения. В заголовке таких функций тип возвращаемого значения объявляется void. Если тип возвращаемого значения не указан, он по умолчанию считается равным int. Пример функции, не возвращающей значения, приведен в программе на рис. 9.3. 76 //Возведение произвольного числа m в степень i #include #include void step(float, int); // прототип функции main( ) { int i; float m, s; clrscr(); // очистка экрана cout<<"\nВведите m = "; cin>> m; cout<<"\nВведите i = "; cin>> i; step(m, i); cout<<"\nНажмите любую клавишу…"; getch(); } void step(float base, int k) // заголовок функции { int i; float p = 1; for(i=1;i<=k;++i) p*=base; cout<<"\n"<< base << " в степени "<< k << " равно "< #include int multiply(int, int &); main( ) { clrscr(); //очистка экрана int x1,y1,z; cout<<"\nВведите первый сомножитель=; cin>>x1; cout<<"\nВведите второй сомножитель="; cin>>y1; cout<<"\nРезультат ="< #include int multiply(int ,int *); // прототип функции main() 81 { clrscr(); //очистка экрана int x1,y1,z; cout<<"\n Введите первый сомножитель="; cin>>x1; cout<<"\nВведите второй сомножитель="; cin>>y1; z=myltip(x1, &y1); // вызов функции cout<<"\nРезультат ="<< z; cout<<"\n -------------------------------- \n"; cout«"\nНажмите любую клавишу…"; getch(); } int multip(int x, int *y) // заголовок функции { return *y*x; // изменение значения параметра } Рис. 9.5 10. Указатели 10.1. Назначение указателей Указатели позволяют применять язык C++ в самом широком диапазоне задач - от драйверов устройств на уровне аппаратного обеспечения и управляющих систем реального времени до операционных систем и компиляторов, анимации и мультимедийных приложений. C++ - это идеальный инструмент для решения задач в поразительно широком многообразии прикладных областей. Указатель - это особый тип переменной, содержащей в памяти адрес того элемента, на который он указывает. При этом имя элемента отсылает к его значению прямо, а указатель косвенно. Поэтому ссылка на значение посредством указателей называется косвенной адресацией. Смысл использования указателей состоит в том, что, отводя место только для запоминания адреса в памяти, Вы получаете идентификатор (переменную типа указатель), который может ссылаться на любой элемент в памяти указанного типа, причем в разный момент указатель мо- 82 жет указывать на разные элементы, т.е. не надо перемещать в памяти с места на место кучи байтов, достаточно просто изменить указатель. Указатель может указывать на любые объекты: переменные, массивы, классы, структуры, и даже на функции. При объявлении указателя необходимо указать тип элемента, на который он будет указывать. Сам указатель занимает ровно столько места, сколько требуется для хранения адреса элемента. Признаком указателя является символ (*), который означает, что следующая за ним переменная является указателем. Рассмотрим примеры объявления указателей для различных типов переменных. int *r; float*f; char*ch; long *g[10]; long (*t)[10]; int(*fun)(double, int ); указатель на целое число, указатель на действительное число, указатель на символьную переменную, массив из 10указателей на длинное целое, указатель на массив из десяти длинных целых, указатель на функцию с именем fun. Для инициализации указателей используется операция присваивания "=". Присваивание начального значения означает, что при определении указателя ему следует присвоить либо адрес объекта, либо адрес конкретного места в памяти, либо число 0. Примеры каждого типа инициализации выглядят так: 1. Присваивание указателю адреса существующего объекта:  С помощью операции получения адреса: int a=5; // целая переменная int*p = &a; // указателю присваивается адрес переменной а int*p (&a); // то же самое другим способом  С помощью значения другого инициализированного указателя:  int*r = p;  С помощью имени массива или функции, которые трактуются как адрес: int b[10]; int *t = b; // массив // присвоение адреса начала массива 2. Присваивание указателю адреса области памяти в явном виде: char*z = (char*)0x00000417; Здесь 0x00000417 - шестнадцатеричная константа , (char*) - операция приведения константы к типу указатель на char. 83 3. Присвоение пустого значения: int*p = 0; Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать для проверки, с помощью которой можно оценить: ссылается указатель на конкретный объект или нет. 4. Выделение участка динамической памяти и присвоение ее адреса указа- телю с помощью операции new: • int*n = new int; // 1 • int*m = new int(10); // 2 • int*q = new int[10]; // 3 В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n. В операторе 2, кроме того, производится инициализация выделенной памяти значением 10. В операторе 3 операция new выделяет память под массив из 10 элементов и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива. 10.2. Операции над указателями С указателями связаны два специальных оператора: & и*. Обе эти операции унарные, т.е. имеют один операнд, перед которым они ставятся. Операция & соответствует действию "взять адрес". Операция * соответствует словам "значение, расположенное по указанному адресу". Например: int y = 5; int *py; py = &y; . Здесь оператор py = &y; присваивает адрес переменной у указателю py. Говорят, что переменная py указывает на y. Оператор * обычно называют оператором косвенной адресации, или операцией разыменования, возвращающей значение объекта (элемента), на который указывает ее операнд (т.е. указатель). Например, оператор cout << *py <<"\n"; печатает значение переменной у, а именно 5. 84 Использование * указанным способом позволяет обеспечить доступ к величине, адрес которой хранится в указателе. На рис. 10.1 приведен листинг программы, в которой рассматриваются примеры использования операций & и *. #include #include main() { int a; // а - целое число int *pa ; // pa - указывает на адрес целого числа clrscr(); a = 7; pa = &a; // pa устанавливаем равным адресу переменной а cout<<"\nАдрес а:"<<&a<<"\n"<<"Значение ра:"< #include main() { int x; // x - целое число int *p ,*p1; // указывают на целые числа clrscr(); // очистка экрана p = &x; // указателю присваивается адрес целого числа х p1=p+3; cout<<"\nНачальное значение р:"< = < p > + n*< количество байт памяти базового типа указателя>. Можно так же вычитать один указатель из другого. Так, если р и р1 - указатели на элементы одного и того же массива, то операция р - р1 дает такой же результат, как и вычитание соответствующих индексов массива. Указатели можно сравнивать, при этом применимы все 6 операций: <, >, <=, >=, =, ==, !=. Сравнение p void main() { int a=1, b=2; int *c=&b, *d=&a; cout<< a <<' '<< b<<' '<<*c«' '<< d<<' '<< c<<' '<< *d<<' '<< "\n"; }_________________________________________________________________ Результаты работы программы: a=1, b=2, *c=2, d=0xfff2, c=0xfff4, *d=1. Рис. 10.3 11. Массивы Массивы - это группа элементов одинакового типа (double, float, int и т.п.), хранящихся под одним именем. Массив характеризуется своим именем, типом хранимых элементов, размером (количеством элементов), нумерацией элементов и размерностью. Различают одномерные и многомерные массивы. Основная форма объявления массива размерности N имеет следующий формат: тип < имя массива> [размер 1][размер2]... [размер N ]; тип - базовый тип элементов массива, [размер1][размер2]... [ размер N] - количество элементов одномерных массивов, входящих в многомерный массив. 11.1. Одномерные массивы Чаще всего используются одномерные массивы, форма объявления которых будет иметь вид Тип <имя массива> [размер]. Например, оператор int A[10]; объявляет массив с именем А, содержащий 10 целых чисел. Доступ к элементам массива осуществляется выражением А[i] , где i - индекс элементов массива, который начинается с нуля и в данном примере заканчивается цифрой 9. Поэтому элемент А[0] характеризует значение первого элемента массива, А[1] - второго, А[9] - по- следнего. Объявление массива можно совмещать с заданием элементам массива начальных значений. Эти значения перечисляются в списке инициализации после знака равенства, Темаяются запятыми и заключаются в фигурные скобки, например: int A[10] = {1,2,3,4,5,6,7,8,9,10};. Элементы массива могут иметь любой 88 тип. Так, например, оператор char S[10]; объявляет массив из символов. Массив символов - это фактически строка, и число символов, помещаемых в строку, должно быть на единицу меньше объявленного размера массива. Это обусловлено тем, что строка кончается нулевым символом и будет, к примеру, иметь вид char S[10] = {"abcdefghi\0"};. Нулевой символ в конце можно не указывать, поэтому нормально будет воспринято такое объявление: char S[10] = {"abcdefghi"};. 11.2. Многомерные массивы Многомерным называется массив, элементами которого являются одномерные массивы. Например, двумерный массив может быть объявлен таким образом: int A2[10][3];. Этот оператор описывает двумерный массив, который можно пред- ставить себе как таблицу, состоящую из 10 строк и 3 столбцов. Доступ к значениям элементов многомерного массива обеспечивается через индексы, каждый из которых заключается в квадратные скобки. Например, A2[3][2] - значение элемента, лежащего на пересечении четвёртой строки и третьего столбца (напоминаем, что индексы начинаются с 0). Если многомерный массив инициализируется при его объявлении, список значений по каждой размерности заключается в фигурные скобки. Приведённый ниже оператор объявляет и инициализирует двумерный массив A2 размерностью 3 на 5: int A2[3][5] = {{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};, однако допустим и упрощенный способ инициализации: int A2[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};. 11.3. Примеры использования массивов Так как массив является структурным типом, то основные операции с ним необходимо проводить с помощью оператора цикла. Покажем это на ряде примеров. В примере на рис. 11.1 с помощью оператора цикла осуществляется присвоение начальных нулевых значений элементам массива n[5], содержащего пять целых чисел. Печать массива осуществляется в табулированном формате. Первый оператор вывода печатает на экране заголовки столбцов, а второй выводит эле89 менты массива и их значения. Функция setw() указывает ширину поля, в котором будет выведено следующее значение. #include #include #include #include main( ) { clrscr(); // очистка экрана int n[5]; for(int i=0;i<5;i++)n[i]=0; // обнуление элементов массива cout<<"элемент"«setw(13)<<"значение\n"; // вывод заголовков for(i=0;i<5;i++) cout< #include #include #include main() { clrscr(); int i; 90 int mes[12]={31,28,31,30,31,30,31,31,30,31,30,31}; // инициализация массива int mcopy[12]; for(i=0;i<12;i++) mcopy[i]=mes[i]; // cout<<"Исходный массив "< #include #include main() { float a=0; float y[10], x[10]; int i=0; clrscr(); cout<<"\nВведите a : "; cin>>a; for( i=0; i<10;i++) { cout<<"\nВведите x["<>x[i]; y[i]= (x[i] + a) / sqrt((x[i]*x[i])+1); cout<<"\t\t\t y["< #include #include main() { float a=0; float y[3][2], x[3][2]; int i=0, j=0; clrscr(); cout<<"\nВведите a : "; cin>> a; // ввод переменной а for( i=0; i<3; i++) { for( j=0; j<2; j++) { cout<<"\nВведите x["<> x[i][j]; // ввод элементов массива x[i][j] y[i][j]=(x[i][j] + a)/sqrt((x[i][j] * x[i][j])+1); cout<<"\t\t\t y["< #include #include main() { float a[10]; int i=0, n=0, p=0, zero=0; // обнуление переменных clrscr(); cout<<"\nОпределить количество положительных и отрицательных элементов массива a[10]\n"; for( i=0; i<10;i++) { cout«"\nВведите a["<>a[i]; } for( i=0; i<10; i++) { if( a[i] > 0) p += 1; // определение количества положительных .элементов if( a[i] < 0) n += 1; // определение количества отрицательных элементов if( a[i] == 0) zero += 1; // определение количества нулевых элементов } cout<<"\n\n"; cout<<"\nЧисло положительных элементов ="< #include #include #define row 2 // строки #define col 3 // столбцы main() { float b[row][col]; // объявление массива int i=0, j=0,n=0, p=0, zero=0; clrscr(); cout<<"\nОпределить количество положительных и отрицательных элементов"; cout<<"\n массива b["< 0) p += 1; if(b[i][j] < 0) n += 1; if(b[i][j] == 0) zero += 1; } } cout<<"\nЧисло положительных элементов = "< #include #include #include #define n 5 vec(int [ ], int); // прототип функции вычисления min компоненты вектора main() { int y[n], i, min; clrscr(); for(i=0;i #include #include #include #define n 2 #define m 3 mas(int [ ][m], int, int); main( ) { int y[n][m], i, j, min ; clrscr(); for(i=0;i #include void main() { clrscr(); int a[5], sum = 0, *p; int kol = 0, i; p = &a[0]; // инициализация указателя адресом первого элемента cout << " Ввод данных в массив a[ ]\n"; for ( i = 0; i <5; i++){ 97 cout << " a [ " << i << " ] = "; cin >> *(p+i); // разыменовывание смещенного указателя } // расчет суммы и количества положительных элементов for ( i = 0; i < 5; i ++) if ( *(p+ i) > 0 ) { sum += *( p+i ); kol ++; } // вывод исходных данных и результатов cout << "\n\n\n Элемент массива Адрес элемента массива \n"; for ( i = 0; i < 5; i++ ){ cout << *( p+ i) << "\t\t " << (p+i) << "\n"; // вывод результатов } cout << "\nсумма = " << sum << "^количество = " << kol; cout<<"\n\n"; cout<<"\nНажмите любую клавишу…"; getch(); } _________________________________________________________________________ Результаты работы программы: Ввод данных в массив a[ ] : a[0]=1 a[1]=2 a[2]=5 a[3]=5 a[4]=4 Элемент массива 1 2 3 4 5 Адрес элемента массива Oxffec Oxffee Oxfff0 Oxfff2 Oxfff4 сумма = 17 количество=5 Рис. 11.9. На рис.11.10 приведен пример программы, в которой с помощью указателей формируется двумерный массив а[2][2], а из минимальных элементов его столбцов формируется массив b[2]. Значения полученных массивов выводятся на дисплей. #include #define I 2 98 #define J 2 #include void main() { clrscr(); int a[I][J], b[J], min, *p ; int i,j; p = &a[0][0]; // инициализация указателя адресом первой ячейки cout << "Введите данные в массив a["<< I <<"]["< #include main() { int n; clrscr(); cout<<" Введите десятичное число"<<" "; cin>>n; cout < #include #include #include main() { int d; double root2 = sqrt(2.0); clrscr(); cout<<" корень квадратный из 2 с точностью d от 0 до 9"< #include #include main() { int n=25; clrscr(); for(int i=0; i< 5; i++) cout< #include #include main() { int n=25; clrscr(); for(int i=0; i< 5; i++){ cout.width(i); 103 cout < #include const int size = 80; main() { clrscr(); char buffer[size] ; cout << "Введите предложение :"< #include void main() { clrscr(); cout << "Введите имя файла для ввода: "; 7. char fname[80]; //создание массива для записи имени файла 8. cin >> fname; 9. ofstream fout(fname); // открыть файл для записи 10. if (!fout) // проверить открытие файла 11. { 12. cout << "Невозможно открыть файл"; 13. return; 14. } 15. char c; 16. cout << "Вводите информацию :\n"; 17. cout << "Конец ввода - символ *\n"; 18. while ( fout ) 19. { 20. fout.put(c); // запись информации в файл 21. cin.get(c); 22. if ( c == '*') 23. break; 24. } 25. fout.close(); 26. cout << "Введите имя файла для считывания : "; 27. cin >> fname; 28. ifstream fin (fname); 29. if ( !fin) 30. { 31. cout << "Невозможно открыть файл"; 32. return; 33. } 34. while ( fin ) 35. { 36. fin.get(c); // считывание информации с файла 110 37. 38. 39. 40. 41. 42. cout << c; } fin.close(); cout<<"\nНажмите любую клавишу... "; getch(); } Рис. 14.1. В строке 7 создается массив для записи имени файла, а в строке 8 предлагается ввести имя файла, которое записывается в массив fname. В строке 9 создается объект ofstream c именем fout, который связывается с введенным ранее именем файла. В строках 10-12 осуществляется проверка открытия файла. При неудачной попытке выводится сообщение: "Невозможно открыть файл". В строках 14-23 осуществляется запись вводимой информации в файл и контроль конца записи. Запись информации осуществляется с помощью функции put(c), которая посимвольно вводит данные в файл. Контроль конца записи реализуется с помощью функции get(c), которая считывает вводимые в файл данные и заносит их в символьную переменную с. Ввод информации в файл продолжается до тех пор, пока в переменной с не появится ключевой символ '*', определяющий конец записи. В строке 25 файл закрывается. В строке 28, после введения имени в строке 26, файл открывается заново, но в этот раз для чтения, и его содержимое посимвольно выводится в программу в строках 34-37. В строке 39 файл закрывается окончательно. Другой пример создания файла последовательного доступа показан на рис. 14.2. В этом примере для каждого клиента программа получает номер счета, имя клиента и сумму денег, которую клиент должен компании за товары и услуги, полученные в прошлом. Данные, полученные от каждого клиента, образуют записи, в которых номер счета используется в качестве ключа записи. Таким образом, файл будет создаваться, и обрабатываться, в соответствии с номером счета. В программе файл должен быть открыт для записи, так что создается объект класса ofstream. Конструктору объекта передаются два аргумента - имя файла и режим открытия файла. Для объекта ofstream режим открытия файла может быть или 111 ios::out - для записи данных в файл, или ios::app - для добавления данных в конец файла. #include #include #include void outputLine(int,char*,float) ; void main() { clrscr(); cout << "Введите имя файла для ввода: "; char fname[80]; // создание массива для записи имени файла cin >> fname; ofstream fout(fname); //открыть файл для записи if (!fout) { // проверить открытие файла cerr << "Невозможно открыть файл"<>accout >>name>>sum) { fout<< accout<<" " <> accout>>name>>sum) { outputLine(accout, name, sum); // запись информации в файл } fin.close(); cout<<"\nНажмите любую клавишу..."; getch(); } void outputLine(int acct, char*name, float bal){ cout<>accout >>name>>sum) вводит каждый набор данных и определяет, не введен ли признак конца файла. Когда достигнут признак конца файла или вводятся неверные данные, условное выражение возвращает 0 и оператор while завершает свою работу. После этого файл закрывается явным образом с помощью функции-элемента clouse: fout.close();. Для чтения файла последовательного доступа его открывают путем создания объекта класса ifstream. Объекту передаются два аргумента - имя файла и режим открытия файла. Объявление ifstream fin ("clients.dat", ios::in); создаёт объект fin класса ifstream, связанный с файлом clients.dat, который открывается для чтения. По умолчанию объекты класса ifstream открыты для чтения, поэтому для открытия файла clients.dat для чтения может быть использован оператор ifstream fin ("clients.dat"); . Строка while (fin>>accout >>name>>sum) читает из файла набор данных, т.е. записи. Всякий раз, когда выполняется оператор while, считывается следующая запись из файла. Записи выводятся на экран с помощью функции output(), которая использует параметризованные манипуляторы потока для форматирования данных, изображаемых на экране. Когда достигается конец файла, входная последовательность в операторе while возвращает 0, и программа выходит из цикла. Файл закрывается оператором close(), и программа завершает свою работу. При поиске данных в последовательном файле программа начинает чтение данных с начала файла и читает все данные последовательно до тех пор, пока не будут найдены требуемые данные. Это приводит к необходимости обрабатывать файл последовательно несколько раз (каждый раз с начала), если искомые данные расположены в разных местах файла. Классы istream и оstream содержат функ114 ции-элементы для позиционирования, которые определяют порядковый номер следующего байта в файле для считывания или записи. Этими функциямиэлементами являются seekg (позиционировать при извлечении из потока) для класса istream и seekp (позиционировать при помещении в поток) для класса оstream. Кроме того, любой объект класса istream имеет указатель get, показыва- ющий номер очередного вводимого в файл байта, а любой объект класса оstream имеет указатель set, показывающий номер очередного выводимого из файла байта. Оператор fin.seekg(0); позиционирует указатель на начало файла. Аргумент функции seekg( ) обычно является целым типа long, а второй аргумент, который может быть задан, показывает направление позиционирования. Оно может быть ios::beg при позиционировании относительно начала потока (используется по умолчанию), ios::cur - для позиционирования относительно текущей позиции и ios::end - для позиционирования относительно конца потока. Примеры: // Позиционировать fileObject на n-й байт(полагаем ios::beg) fileObject . seekg(n ); // Позиционировать fileObject на n байтов вперед fileObject . seekg(n , ios::cur ); // Позиционировать fileObject на i-й байт от конца файла fileObject . seekg(n , ios::end); // Позиционировать fileObject на конец файла fileObject.seekg(0 , ios::end); Те же самые операции могут быть выполнены с помощью функцииэлемента seekp класса оstream. Функции-элементы tellg и tellp возвращают текущие позиции указателей соответственно get и set. Следующий оператор присваивает переменной location = fileObject.tellg(); значение указателя get. Программа на рис. 14.3 позволяет менеджеру по кредитам отображать на экране информацию о клиентах с нулевой задолженностью, информацию о клиентах-задолжниках и клиентах, которым должна компания. Программа отображает меню и позволяет по каждой из трех опций получить соответствующую информацию по кредитам. 115 // Программа запроса кредитной информации #include #include #include #include #include void output( int , char* ,float ) ; int main() { clrscr(); ifstream fin("Clients"); //открытие файла для записи if(!fin){ cerr<<" файл не открыть"; exit(1); } cout<<"Запрос на ввод \n" <<"1-список счетов с нулевым балансом\n" <<"2- список счетов c кредитным балансом\n" <<"3- список счетов с дебитовым сальдо\n" <<"4 -конец счета \n"; int zapros; cin >>zapros; while(zapros !=4){ int account; char name[10]; float sum; fin>>account>>name>>sum; switch( zapros){ case 1: cout<>account>>name>>sum; } break; case 2: cout<>account>>name>>sum; } break; case 3: 116 cout< 0) output(account, name, sum ); fin>>account>>name>>sum; } break; } fin.clear(); fin.seekg(0); cout<> zapros ; } cout<<"\nНажмите любую клавишу…"; getch(); return 0; } void output(int a, char* n, float s) { cout< #include #include #include #include void output(int, char*, float); //прототип функции ввода struct Vkladchik { // определение структуры "Вкладчик" int account; // номер счета char name[10]; // имя float suma; // сумма вклада }; int main() { clrscr(); Vkladchik k ; // создание экземпляра объекта cout<<"Введите счет, имя , сумму \n"; cin>>k.account>>k.name>>k.suma; cout<<"Счет"«setw(9 )<<"Имя" < #include #include #include #include void output(int , char* ,float ) ; struct Vkladchik { // определение структуры "Вкладчик" int account; // номер счета char name[10]; // имя float suma; // сумма вклада }; int main() { int i, n; clrscr(); cout<<"Введите число записей n= "; cin>>n; Vkladchik k[10]; // создание массива экземпляров объектов cout<<"Введите счет, имя, сумму \n"; for(i=0;i>k[i].account>>k[i].name>>k[i].suma; cout<<"?"< #include #include #include #include void output(int, char*, char*, int ); // прототип функции вывода struct book{ // определение структуры int nzap; // номер записи char fam[20]; // фамилия char dol[10]; // должность int otdel; // отдел }; int main(){ clrscr(); book b; // создание экземпляра объекта ofstream outfile("kniga"); //открытие файла для записи if(!outfile){ cerr<<"файл не открыть"; exit(1); } 122 cout<<"Введите номер записи, фамилию, должность, номер отдела" <<" и символ eof по окончании ввода\n"; while(cin>>b.nzap>>b.fam>>b.dol>>b.otdel){ outfile«b.nzap<<" "<>b.nzap>>b.fam>>b.dol>>b.otdel) output(b.nzap, b.fam, b.dol, b.otdel); // вызов функции вывода infile.close(); cout<<"\n\n"; cout<<"Нажмите любую клавишу…"; getch(); return 0; } void output(int z, char* a, char* n, int s) { // описание функции вывода cout< #include #include #include #define FAM 25 #define DOL 15 // объявление структуры -."Сотрудники " struct SOTR{ char fam [FAM]; // фамилия char dol[DOL]; // должность // отдел int otdel; }; void sozdanie(); //прототип функции : " Создание " void prosmotr(); // прототип функции : " Просмотр " // операция-функция ввода в структуру с клавиатуры istream &operator >> (istream &in, SOTR &x) { cout<<"\nФамилия:"; in.seekg(0,ios::end); in.get(x.fam,FAM-1,'\n'); cout<<"\nДолжность:"; in.seekg(0,ios::end); in.get(x.dol,DOL-1,'\n'); cout<<"\nОтдел:"; in.seekg(0,ios::end); in >> x.otdel; return in; } / / операция-функция вывода структуры на дисплей ostream &operator << (ostream &out, SOTR x){ / / печать объекта out << "\n|" << x.fam << "|" << x.dol << "|" << x.otdel <<"|"; return out; } / / операция-функция ввода структуры c МД ifstream &operator >> (ifstream &in, SOTR &x){ in.setf(ios::left); in.width(FAM); in.get(x.fam,FAM,'\n'); in.width(DOL); in.get(x.dol,DOL,'\n'); in >> x.otdel; return in; } / / операция-функция вывода структуры на МД ofstream &operator << (ofstream &out, SOTR &x) { out.width(FAM-1); out.setf(ios::left); out << x.fam; out.width(DOL-1); out.setf(ios::left); out << x.dol; out << x.otdel; return out; 124 } void main(void) { clrscr(); char c; while (1) { cout << endl << "1. Создание файла"; cout << endl << "2. Просмотр содержимого"; cout << endl << "3. Выход"; cout << endl << "Ваш выбор -> "; cin.seekg(0,ios::end); c = cin.get(); switch(c){ case '1': sozdanie(); break; case '2': prosmotr(); break; case '3': return; default: cout << "Вводите только цифры от 1 до 3" << endl; } } } void sozdanie(){ char c; // поток ff для вывода файла kniga.txt ofstream ff; // создание экземпляра объекта SOTR s; ff.open("kadry.txt", ios::binary ); // цикл записи элементов в файл do{ cin >> s; // ввод с клавиатуры ff << s; // вывод в файл cout<<"\nПродолжить ввод?(Y/N\ или Д/Н)"; } while ((c = getch())=='y'||c=='Y'||c=='д'||c=='Д'); ff.close(); // закрытие файла } void prosmotr(){ ifstream finp; 125 SOTR s; // поток finp для ввода из файла kniga.txt finp.open("kadry.txt", ios::binary); finp.seekg(0,ios::beg); cout<<"\nСписок элементов из файла\n"; while ( finp ) { // пока не конец файла finp >> s ; // вывод из файла cout << s; // вывод на дисплей } finp.close(); // закрытие файла } ________________________________________________________________ Результаты работы программы: 1. Создание файла 2. Просмотр содержимого 3. Выход Ваш выбор -> 1 Фамилия: Иванов Должность: лаборант Отдел: 12 Продолжить ввод?(Y/N или Д/Н) Y Фамилия: Петров Должность: инженер Отдел: 15 продолжить ввод?( Y/N или Д/Н) N 1. Создание файла 2. Просмотр содержимого 3. Выход Ваш выбор -> 2 Фамилия Иванов Петров Должность лаборант инженер Отдел 12 15 Рис. 15.4 126 16. Классы 16.1. Определение класса Программисты на С основное внимание уделяют написанию функций, тогда как при программировании на С++ большее внимание уделяется созданию классов. Классы - это типы данных, определяемые программистом. Каждый класс содержит данные и набор функций, манипулирующих с этими данными. Компоненты - данные класса называются данными-элементами. Компоненты - функции класса называются функциями-элементами. Класс, определяемый пользователем, называется объектом. Классы в С++ являются естественным продолжением структуры (struct) в С. Прежде чем рассматривать специфику разработки классов, проанализируем недостатки структуры от которых удается легко избавиться в классах. Программа на рис. 16.1 создает определенный пользователем тип структуры Time с тремя целыми элементами: hour, minute, second. //Создание структуры, задание и печать ее элементов #include #include struct Time { // определение структуры int hour; // 0-23 int minute; // 0-59 int second; // 0-59 }; void printMilitary (int, int, int); // прототип void printStandard (int, int, int); // прототип main (){ Time dinnerTime; // переменная нового типа Time dinnerTime.hour = 18; // задание элементам правильных значений dinnerTime.minute = 30; dinnerTime.second = 0; cout << "Обед состоится в "; printMilitary(dinnerTime.hour, dinnerTime.minute, dinnerTime.second ); cout << "по военному времени," << endl << "что соответствует"; cout << "по стандартному времени," << endl; 127 printStandard(dinnerTime.hour, dinnerTime.minute, dinnerTime.second); dinnerTime.hour = 29; // задание элементам неправильных значений dinnerTime.minute = 73; dinnerTime.second = 103; cout << endl << "Время с неправильными значениями:"; printMilitary(dinnerTime.hour, dinnerTime.minute, dinnerTime.second); cout << endl; getch(); return 0; } // Печать времени в военном формате void printMilitary (int h, int m, int s){ cout << ( h < 10 ? "0" : "") << h << ":" << (m < 10 ? "0" : "") << m << ":" << (m < 10 ? "0" : "") << s ; } // Печать времени в стандартном формате void printStandard(int h, int m, int s){ cout << ( h == 0 || h == 12 ? 12 : h %12 ) << ":" << (m < 10 ? "0" : "") << m << ":" << (m < 10 ? "0" : "") << s ; } ____________________________________________________________ Результаты работы программы: Обед состоится в 18: 30: 00 по военному времени, что соответствует 6: 30: 00 РМ по стандартному времени. Время с неправильными значениями: 29: 73: 103 Рис. 16.1. Программа определяет единственную структуру типа Time, названную dinnerTime, и использует операцию точка для присвоения элементам структуры начальных значений 18 для hour, 30 для minute и 0 для second. Затем программа печатает время в военном (24-часовом) и стандартном (12-часовом) форматах. Основной недостаток структур связан с тем, что в них не существует никакого интерфейса по оценке правильности использования типов данных и оценки противоречивости их начальных значений. Существуют и другие проблемы, связанные со структурами в стиле С. В С структуры не могут быть напечатаны как еди- 128 ное целое, а их печать возможна только по одному элементу с соответствующим форматированием каждого. Классы предоставляют программисту возможность моделировать объекты, которые имеют атрибуты, представленные как данные - элементы и варианты поведения или операции, представленные как функции - элементы. Типы, содержащие данные - элементы и функции - элементы, определяются в С++ с помощью ключевого слова classs. Когда класс определен, имя класса может быть использовано для объявления объекта этого класса. На рис. 16.2 дано простое определение класса Time. Определение класса начинается с ключевого слова classs. В фигурных скобках записывается тело класса, а его определение заканчивается точкой с запятой. Определение класса Time, как и в структуре, содержит три целых элемента: hour, minute и second. //Простое определение класса Time class Time { public: Time(); void setTime (int, int, int); void printMilitary(); void printStandart(); private: int hour; // 0-23 int minute; // 0-59 int second; // 0-59 }; Рис. 16.2 Остальные части определения класса новые. Метки public: (открытая) и private: (закрытая) называются спецификаторами доступа к элементам. Любые данные и функции-элементы, объявленные после спецификатора доступа public:, доступны при любом обращении программы к объекту класса Time, а эти же эле-менты, объявленные после спецификатора доступа private:, доступны только функциямэлементам этого класса. Спецификаторы доступа к элементам всегда заканчиваются двоеточием и могут появляться в определении класса много раз и 129 в любом порядке. Определение класса (рис.16.2) содержит после спецификатора доступа public: прототипы следующих четырёх функций элементов: Time, setTime, printMilitary, printStandart. Это - открытые функции-элементы или открытый ин- терфейс услуг класса. Эти функции будут использоваться для манипуляций с данными класса. Функция-элемент с тем же именем, что и класс, называется конструктором этого класса. Конструктор – это специальная функция-элемент, которая присваивает начальные значения данным-элементам этого класса. После спецификатора private: следуют три целых элемента. Они являются доступными только функциям-элементам класса, т. е. функциям, прототипы которых включены в определение этого класса. Когда класс определен, его можно использовать в качестве типа в объявлениях, например, следующим образом: Time sunset. ArrayOfTimes[5] . *pointerToTime. &dinnerTime = sunset; // объект типа Time // массив объектов типа Time // указатель на объект типа Time // ссылка на объект типа Time Программа на рис.16.3 использует класс Time и создает единственный объект класса Time, названный t. После создания объекта, автоматически вызывается конструктор Time, который явно присваивает нулевые начальные значения всем данным-элементам закрытой части private. Затем печатается время в военном и стандартном форматах, с тем, чтобы подтвердить, что элементы получили правильные начальные значения. После этого с помощью функции-элемента setTime устанавливается время, и оно снова печатается в обоих форматах. Затем функцияэлемент setTime пытается дать данным-элементам неправильные значения, и время снова печатается в обоих форматах. // Определение класса Time class Time { public : Time ( ); void setTime(int, int, int ); void printMilitary( ); void printStandard( ); // конструктор // установка часов, минут и секунд // печать времени в военном и // стандартном форматах 130 private: int hour ; int minute; int second; }; // 0 - 23 // 0 - 59 // 0 - 59 /*Конструктор Time ( ) ; присваивает нулевые начальные значения каждому элементу данных и обеспечивает согласованное начальное состояние всех объектов Time ( ) ; */ Time :: Time( ) { hour = minute = second = 0; } / * Задание нового значения Time в военном формате. Проверка правильности значений данных. Обнуление неверных значений. */ void Time : : setTime( int h, int m, int s ){ hour = ( h>= 0 && h < 24 ) ? h : 0; minute = (m>=0 && m<60) ? m : 0; second = (s >= 0 && s < 60) ? s : 0; } // Печать времени в военном формате void Time : : printMilitary( ){ cout << ( hour < 10 ? "0" : "") << hour << ":" << (minute < 10 ? "0" : "") << minute << ":" << (second < 10 ? "0" : "") << second ; } // Печать времени в стандартном формате void Time : : printStandard( ){ cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 ) << ":" << (minute < 10 ? "0" : "") < #include 133 class Count { // простой класс Count public: int x; void print () { cout << x << endl; } }; main ( ){ Count counter, // создает объект counter * counterPtr = &counter , // указатель на counter & counterRef = counter; // ссылка на counter cout << " Присвоение x значения 7 и печать по имени объекта :"; counter.x = 7; // присвоение 7 элементу данных x counter.print ( ) ; // вызов функции-элемента для печати cout << " Присвоение x значения 8 и печать по ссылке :"; counterRef.x = 8; // присвоение 8 элементу данных x counter.print ( ) ; // вызов функции-элемента для печати cout << " Присвоение x значения 10 и печать по указателю :"; counterPtr ->x = 10; // присвоение 10 элементу данных x counterPtr->print ( ) ; // вызов функции-элемента для печати } ___________________________________________________________________ Результаты работы программы: Присвоение x значения 7 и печать по имени объекта : 7 Присвоение x значения 8 и печать по ссылке : 8 Присвоение x значения 10 и печать по указателю : 10 Рис . 16.4 На рис 16.5 показан усложненный вариант этой программы на примере класса Clients (Клиенты). В этом классе используются открытые элементы данных account (счет) типа int и sum(сумма) типа float, а также открытая функция-элемент print( ). Программа создает три экземпляра переменных типа Clients -vcladchik (вкладчик), vcladchikRef (ссылка на объект типа Clients) и vcladchik Ptr (указатель на объект типа Clients). Переменная vcladchik Ref объявлена, чтобы ссылаться на vcladchik, а переменная vcladchik Ptr объявлена, чтобы указывать на vcladchik. // Демонстрация операций доступа к элементам класса . и -> #include #include #include #include class Clients{ public: // простой класс Clients 134 int account; char name[20]; float sum; void print () { cout<account = 3; // присвоение 3 элементу данных account strcpy(vcladchikPtr->name,"Mary"); vcladchikPtr ->sum = 858.35; // присвоение значения элементу данных sum vcladchikPtr ->print ( ) ; // вызов функции - элемента для печати getch(); return 0; } _________________________________________________________________________ Результаты работы программы: Присвоение счет =1,имя= Bobby, сумма=125.45 и печать по имени объекта: счет= 1 имя = Bobby сумма = 125.45 Присвоение счет =2,имя=Каt, сумма=458.95 и печать по ссылке: счет= 2 имя = Bobby сумма = 125.45 Присвоение счет =3,имя=Магу, сумма=858.35 и печать по указателю: счет= 1 имя = Boby сумма = 125.45 Рис. 16.5. 135 На рис. 16.6 показан вариант программы, в которой с помощью функцииэлемента print() осуществляется форматированный вывод данных на печать. Как и ранее, функция print() не получает никаких аргументов, потому что она печатает данные-элементы определенного объекта типа Clients. Это уменьшает вероятность появления ошибки при передаче аргументов. В этом классе используются открытые элементы данных account (счет) типа int и sum (сумма) типа float, а также открытая функция - элемент print( ). Программа создает три экземпляра переменных типа Clients - vcladchik(вкладчик), vcladchikRef (ссылка на объект типа Clients) и vcladchikPtr (указатель на объект типа Clients). Переменная vcladchikRef объявлена, чтобы ссылаться на vcladchik, а переменная vcladchikPtr объявлена, чтобы указывать на vcladchik. // Демонстрация операций доступа к элементам класса . и -> #include #include #include #include #include class Clients { // простой класс Clients public: int account; char name[20]; float sum; void print (); }; //описание функции-элемента рrint(), принадлежащей классу Clients void Clients::print () { cout<<"\nсчет "<account = 3; // присвоение 3 элементу данных account strcpy(vcladchikPtr->name,"Mary"); vcladchikPtr ->sum = 858.35; //присвоение значения элементу данных sum vcladchikPtr ->print ( ) ; // вызов функции - элемента для печати getch(); return 0; } void print(int a, char* n, float s) { cout<<" счет "< #include #include #include #include class Clients // простой класс Clients { public: int account; char name[20]; float sum; void print (); }; // описание функции-элемента print ();принадлежащей классу Clients void Clients::print (){ cout<>vcladchik[i].account>>vcladchik[i].name>>vcladchik[i].sum; cout<<"?"<= 0 && h < 24 ) ? h : 0; minute = (m>=0 && m<60) ? m : 0; second = (s >= 0 && s < 60) ? s : 0; } void Time : : printMilitary( ) // печать времени в военном формате { cout << ( hour < 10 ? "0" : "") << hour << ":" << (minute < 10 ? "0" : "") << minute << ":" << (second < 10 ? "0" : "") << second ; } 140 void Time : : printStandard( ) // печать времени в стандартном формате { cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 ) << ":" << (minute < 10 ? "0" : "") < /* Функция-конструктор для задания начальных значений закрытых данных, вызывает функцию-элемент setTime, чтобы установить значения переменных, которые по умолчанию равны нулю.*/ Time :: Time( int hr, int min, int sec) { setTime( hr, min, sec) ; } // Установка значений часов, минут, секунд void Time::setTime(int h, int m, int s) { hour = (h >= && h<24) ? h : 0 ; minute = (m>= 0 && m< 60) ? m : 0; second = (s >=0 && s < 60) ? s : 0; }; // Установка значения часа void Time::setHour(int h) { hour = (h>= 0 && h < 24) ? h : 0;} // Установка значения минут void Time:: setMinute(int m) { minute = (m>=0 && m<60) ? m : 0;} // Установка значения секунд void Time:: setSecond(int s) { second = (s >= 0 && s < 60) ? s : 0;} //Получение значения часа int Time :: getHour() {return hour;} int Time :: getMinute () {return minute;} int Time :: getSecond () {return second;} // Печать времени в военном формате void Time :: printMilitary( ) { cout << ( hour < 10 ? "0" : "") << hour << ":" << (minute < 10 ? "0" : "") << minute << ":" << (second < 10 ? "0" : "") << second ; } // Печать времени в стандартном формате void Time :: printStandard( ) { cout << ( (hour == 0 || hour == 12 ) ? 12 : hour %12 ) << ":" << (minute < 10 ? "0" : "") < // #include "time3. h " void incrementMinutes( Time &, int); main () { Time t; t.setHour(17); t.setMinute(34); t.setSecond(25); cout << "Результат установки всех правильных значений:"<< endl <<" Час: " << t.getHour() <<" Минуты: " << t.getMinute () <<" Секунды: " << t.getSecond () << endl < class Count { friend void setX(Count &, int); //объявление друга public: Count() { x = 0; } // конструктор void print() const {cout << x << endl; } // вывод private : int x; // элемент данных }; void setX(count &c, int val) { c.x = val; } main () { count object; cout << "object.x после своего создания :"; object.print(); 147 cout << "object.x после вызова дружественной функции setX:"; setX(object, 8); object.print(); return 0; } ___________________________________________________________________ object.x после своего создания: 0 object.x после вызова дружественной функции setX: 8 Рис. 16.10 Другой пример использования дружественной функции для доступа к закрытому элементу приведен на рис.16.11. В программе используется дружественная функция SUM() для установки закрытого элемента данных sum (сумма вклада) класса Clients. Конструктор задает закрытому элементу данных sum начальное нулевое значение. Передача параметров в дружественную функцию осуществляется по ссылке. // Пример использования дружественной функции #include #include #include #include #include // Простой класс Clients class Clients { friend void SUM (Clients& , float ) ; //объявление дружественной функции public: Clients() { sum=0; } // конструктор // Открытые данные - элементы int account; // номер счета char name[20]; // массив символьных переменных void print (); // функция-элемент private: float sum; // закрытые данные-элементы }; void SUM(Clients& c, float val) // описание дружественной функции { c.sum = val; }; void Clients::print () // описание функции-элемента 148 { cout<
«Понятие об алгоритмах, определение и свойства алгоритма» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Найди решение своей задачи среди 1 000 000 ответов
Найти
Найди решение своей задачи среди 1 000 000 ответов
Крупнейшая русскоязычная библиотека студенческих решенных задач

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot