Учебная работа. Реферат: Основы программирования на языке Си
Основы программирования
на языкеСи++
(длястудентовфизико-математическихфакультетов
педагогических институтов)
Коломна, 2002
2
ББК 32.97я73
Рекомендовано кизданию
УДК 681.142.2(075.8) редакционно-издательскимсоветом
Б 73 Коломенскогогосударственного
педагогического института
Богуславский А.А., СоколовС.М.
Б73 ОсновыпрограммированиянаязыкеСи++: Длястудентовфизико-
математических факультетовпедагогическихинститутов. –Коломна: КГПИ,
2002. – 490 с.
Пособие предназначенодляобучениястудентов, обладающих навыкамиполь-
зовательской работынаперсональномкомпьютере, основнымпонятиямиметодам
современного практическогопрограммирования. Предметомизучениякурсаявляется
объектно-ориентированноепрограммированиенаязыкеСи++ всредесовременных
32-хразрядныхоперационныхсистемсемейства Windows. Программакурсаразбита
на 4 части: (1) ВведениевпрограммированиенаязыкеСи++; (2) Основыпрограмми-
рования трехмернойграфики; (3) Объектно-ориентированное программированиена
языке Си++ и (4) Программированиедля Microsoft Windows сиспользованием Visual
C++ ибиблиотекиклассов MFC.
После изучениякурсастудентполучаетдостаточнополноепредставлениео
содержании современногообъектно-ориентированногопрограммирования, обуст-
ройстве современныхоперационныхсистем Win32 иособытийно-управляемомпро-
граммировании. Напрактическихзанятияхвырабатываютсянавыкипрограммирова-
ния наСи++ винтегрированной средеразработки Microsoft Visual C++ 5.0.
Рецензенты:
И.П. Гиривенко–к.т.н., доцент, зав. кафедройинформатикиивычислительнойтех-
ники Рязанскогогосударственногопедагогическогоуниверситета
им. С.А. Есенина.
А.А. Шамов–к.х.н., доценткафедрытеоретическойфизикиКоломенскогогосу-
дарственного педагогическогоинститута.
3
СОДЕРЖАНИЕ
КРАТКОЕ ОПИСАНИЕ УЧЕБНОГО КУРСА «ОСНОВЫ ПРОГРАММИРОВАНИЯ
НА ЯЗЫКЕ СИ++» …………………………………………………………………………………………………………..5
ЛЕКЦИЯ 1. ОСНОВЫ СИ++……………………………………………………………………………………………7
1. несколько ЗАМЕЧАНИЙОНАЗНАЧЕНИИПРОГРАММИРОВАНИЯ…………………………………………7
2. ПРОИСХОЖДЕНИЕ ЯЗЫКАСИ++………………………………………………………………………………………9
3. СТАНДАРТ ANSI СИ++ ………………………………………………………………………………………………….9
4. СРЕДА РАЗРАБОТКИMICROSOFT DEVELOPER STUDIO VISUAL С++…………………………………….10
5. ПРИМЕР ПРОГРАММЫНАСИ++ …………………………………………………………………………………….10
6. ВЫПОЛНЕНИЕ ВВОДА/ВЫВОДА ДАННЫХИПРИСВАИВАНИЕЗНАЧЕНИЙ………………………………12
7. УПРАВЛЕНИЕ ПОРЯДКОМВЫПОЛНЕНИЯКОМАНДСПОМОЩЬЮОПЕРАТОРА IF ……………………13
8. ОФОРМЛЕНИЕ ИСХОДНОГОТЕКСТА……………………………………………………………………………….15
9. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………15
10. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………15
ЛЕКЦИЯ 2. ПЕРЕМЕННЫЕ, ТИПЫ ДАННЫХ И ВЫРАЖЕНИЯ………………………………18
1. ИДЕНТИФИКАТОРЫ ……………………………………………………………………………………………………..18
2. ТИПЫ ДАННЫХ……………………………………………………………………………………………………………18
3. ВЫВОД ВЕЩЕСТВЕННЫХЧИСЕЛНАЭКРАН……………………………………………………………………..22
4. ОПИСАНИЯ, КОНСТАНТЫ ИПЕРЕЧИСЛЕНИЯ……………………………………………………………………24
5. ПРИСВАИВАНИЕ ИВЫРАЖЕНИЯ…………………………………………………………………………………….26
6. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………28
7. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..28
8. ПРИЛОЖЕНИЯ ……………………………………………………………………………………………………………..29
ЛЕКЦИЯ 3. ФУНКЦИИ И ПРОЦЕДУРНАЯ абстракция ………………………………………31
1. НАЗНАЧЕНИЕ ПОДПРОГРАММ………………………………………………………………………………………..31
2. ОПРЕДЕЛЕНИЕ НОВЫХФУНКЦИЙ…………………………………………………………………………………..31
3. СПОСОБЫ ПЕРЕДАЧИПАРАМЕТРОВВНУТРЬФУНКЦИЙ……………………………………………………..33
4. ПОЛИМОРФИЗМ ИПЕРЕГРУЗКАФУНКЦИЙ……………………………………………………………………….35
5. ПРОЦЕДУРНАЯ АБСТРАКЦИЯИ»ХОРОШИЙ» СТИЛЬ ПРОГРАММИРОВАНИЯ………………………….36
6. МОДУЛЬНОЕ ПРОГРАММИРОВАНИЕ……………………………………………………………………………….36
7. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………38
8. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..39
ЛЕКЦИЯ 4. ТЕКСТОВЫЕ ФАЙЛЫ И ПОТОКИ ВВОДА/ВЫВОДА …………………………..41
1. НАЗНАЧЕНИЕ ФАЙЛОВ………………………………………………………………………………………………….41
2. ПОТОКИ ВВОДА/ВЫВОДА ……………………………………………………………………………………………..41
3. ПРОВЕРКА ОШИБОКВЫПОЛНЕНИЯФАЙЛОВЫХОПЕРАЦИЙ……………………………………………….43
4. СИМВОЛЬНЫЙ ВВОД/ВЫВОД …………………………………………………………………………………………44
5. ПРОВЕРКА ДОСТИЖЕНИЯКОНЦАФАЙЛАПРИОПЕРАЦИЯХВВОДА……………………………………..45
6. ПЕРЕДАЧА ПОТОКОВ ФУНКЦИЯМВКАЧЕСТВЕПАРАМЕТРОВ……………………………………………..47
7. ОПЕРАТОРЫ ВВОДА/ВЫВОДА «>>» И «<<» ………………………………………………………………………48
8. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………50
9. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..50
ЛЕКЦИЯ 5. ОПЕРАТОРЫ ВЕТВЛЕНИЯ И ЦИКЛЫ …………………………………………………..52
1. ЛОГИЧЕСКИЕ значения, ВЫРАЖЕНИЯ ИФУНКЦИИ………………………………………………………….52
2. ЦИКЛЫ «FOR», «WHILE» И «DO…WHILE» ………………………………………………………………………….53
3. МНОЖЕСТВЕННОЕ ВЕТВЛЕНИЕИОПЕРАТОР»SWITCH» ……………………………………………………..55
4. БЛОКИ ИОБЛАСТЬВИДИМОСТИПЕРЕМЕННЫХ………………………………………………………………..56
4
5. ЗАМЕЧАНИЕ ОВЛОЖЕННЫХЦИКЛАХ……………………………………………………………………………..59
6. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………59
7. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..60
ЛЕКЦИЯ 6. МАССИВЫ И СИМВОЛЬНЫЕ СТРОКИ…………………………………………………63
1. НАЗНАЧЕНИЕ МАССИВОВ……………………………………………………………………………………………..63
2. ПЕРЕДАЧА МАССИВОВВКАЧЕСТВЕПАРАМЕТРОВФУНКЦИЙ…………………………………………….66
3. СОРТИРОВКА МАССИВОВ……………………………………………………………………………………………..68
4. ДВУМЕРНЫЕ МАССИВЫ………………………………………………………………………………………………..69
5. СИМВОЛЬНЫЕ СТРОКИ…………………………………………………………………………………………………70
6. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………73
7. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..73
ЛЕКЦИЯ 7. УКАЗАТЕЛИ………………………………………………………………………………………………75
1. НАЗНАЧЕНИЕ УКАЗАТЕЛЕЙ…………………………………………………………………………………………..75
2. ПЕРЕМЕННЫЕ ТИПА»МАССИВ». АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИСУКАЗАТЕЛЯМИ………………..79
3. ДИНАМИЧЕСКИЕ МАССИВЫ………………………………………………………………………………………….81
4. АВТОМАТИЧЕСКИЕ ИДИНАМИЧЕСКИЕПЕРЕМЕННЫЕ……………………………………………………….82
5. СВЯЗНЫЕ СПИСКИ……………………………………………………………………………………………………….82
6. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………86
7. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..87
ЛЕКЦИЯ 8. РЕКУРСИЯ…………………………………………………………………………………………………89
1. понятие РЕКУРСИИ…………………………………………………………………………………………………….89
2. ПРОСТОЙ ПРИМЕРРЕКУРСИИ………………………………………………………………………………………..89
3. КАК ВЫПОЛНЯЕТСЯРЕКУРСИВНЫЙВЫЗОВ……………………………………………………………………..90
4. ЕЩЕ ТРИПРИМЕРАРЕКУРСИИ……………………………………………………………………………………….92
5. РЕКУРСИЯ ИЦИКЛЫ……………………………………………………………………………………………………..93
6. РЕКУРСИЯ ВСТРУКТУРАХДАННЫХ………………………………………………………………………………..94
7. РЕКУРСИВНАЯ РЕАЛИЗАЦИЯАЛГОРИТМАБЫСТРОЙСОРТИРОВКИ………………………………………94
8. СВОДКА РЕЗУЛЬТАТОВ…………………………………………………………………………………………………97
9. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………..97
ЛЕКЦИЯ 9. СОСТАВНЫЕ ТИПЫ ДАННЫХ……………………………………………………………..100
1. НАЗНАЧЕНИЕ СОСТАВНЫХТИПОВДАННЫХ………………………………………………………………….100
2. ОПИСАНИЕ ИИНИЦИАЛИЗАЦИЯСТРУКТУР……………………………………………………………………100
3. ДОСТУП ККОМПОНЕНТАМСТРУКТУРЫЧЕРЕЗУКАЗАТЕЛЬ………………………………………………103
4. МАССИВЫ ИСТРУКТУРЫ…………………………………………………………………………………………….104
5. ПЕРЕГРУЗКА ОПЕРАТОРОВ…………………………………………………………………………………………..105
6. ПРИМЕНЕНИЕ СТРУКТУРДЛЯРЕАЛИЗАЦИИСТЕКА …………………………………………………………107
7. СВОДКА РЕЗУЛЬТАТОВ……………………………………………………………………………………………….111
8. УПРАЖНЕНИЯ ……………………………………………………………………………………………………………112
приложение. КРАТКОЕ РУКОВОДСТВО ПО СРЕДЕ РАЗРАБОТКИ DEVELOPER
STUDIO VISUAL C++……………………………………………………………………………………………………113
1. СОЗДАНИЕ НОВОГОПРОЕКТА………………………………………………………………………………………113
2. ДОБАВЛЕНИЕ ВПРОЕКТНОВОГОИСХОДНОГОФАЙЛА…………………………………………………….114
3. СБОРКА ПРОЕКТА………………………………………………………………………………………………………115
4. ЗАПУСК НОВОГОПРИЛОЖЕНИЯ……………………………………………………………………………………116
ЛИТЕРАТУРА……………………………………………………………………………………………………………..117
5
Краткое описание учебного курса «Основы программиро-
вания на языке Си++»
Развитие современныхтехнологийпрограммированияпредполагаетвладение
программистом широкимнаборомпрактическихнавыков, средикоторыходнимииз
основных можносчитатьзнаниеязыкапрограммирования, средыразработкиисис-
темных технологийбазовойоперационнойсистемы. Рассматриваемыйучебный курс
предназначен дляначальнойподготовкипрограммиста, владеющегоязыкомпро-
граммирования Си++ применительнокразработкепрограммвОСсемейства Win32.
При анализедоступныхвИнтернетзарубежныхкурсов, связанныхсобучени-
ем практическомупрограммированиюврамкахподготовкипоспециальностям Computer
Science, оказаласьзаметнаследующаятенденция: существуюткурсыпоизуче-
нию языкаСи++, курсыпоизучениюобъектно-ориентированногопрограммирования
на базе, чащевсего, Java иреже, Си++, ипрактическистандартныйкурс «Операцион-
ные системы«, посвященныйструктуре Unix-совместимыхоперационныхсистем. Ха-
рактерной особенностьюзарубежныхуниверситетскихкурсовявляетсяотсутствие
разделов, посвященныхизучениюпрактическогопрограммированиявсредекоммер-
ческих ОСмассовогораспространения, впервуюочередь, ОС Windows. Сдругой
стороны, изучениепрограммированиядляэтихОСпредлагаетсярядом коммерческих
учебных организаций, носрокиобученияпорядка 3-5 днейпредполагаютобучение
уже грамотногоспециалиста, имеющегонавыкипрограммированиявкакой-либодру-
гой ОСилинадругомязыкепрограммирования.
В сегодняшнихроссийскихусловиях, неотрицаянеобходимостифундамен-
тальной подготовкиспециалистовповычислительнойтехникевобластитеорииалго-
ритмов иустройствавычислительныхипрограммныхсистем, можноотметитьполез-
ность изучения технологийпрактическогопрограммирования–использованиясред
разработки ибиблиотекпрограммированиядляОСмассовогораспространения. Этим
обусловлена направленностьрассматриваемогокурса–начальнаяподготовкапро-
граммистов наСи++ длясредыОС Windows.
Сложность обученияпрактическомупрограммированию, нанашвзгляд, за-
ключается втрудностисочетанияобученияабстрактнымпонятиямпрограммирова-
ния (таким, какструктурыданных, понятие алгоритма, основныекомпонентыалго-
ритмического языка, методологияпроектированияпрограммногообеспечения), с
изучением технологийисредпрограммированиянабазекакой-либоконкретнойОС.
Эти практическиетехнологиипребываютвпостоянномразвитии, поэтомуможет
быть сложновыделитькакие-либоконкретныесредстваразработкивкачествепред-
мета изучения.
Несмотря наширокоераспространениесредбыстройразработкиПрО (напри-
мер, Visual Basic, Inprise Builder и Inprise Delphi), выборихвкачествеучебнойсреды
представляется нецелесообразным, т.к. вэтихсредахкажущаясяпростотаразработки
ПрО касаетсятолькоформированиякаркасаприложенияизнабораготовыхкомпо-
нент, аустройствоэтихкомпонентилиизменениеструктурыкаркасатребуетсерьез-
ных знанийнетолькопоструктуребазовойОС, ноипосистемнойархитектуресреды
программирования.
программа данногокурсапредназначенадляобучениялиц, имеющих навыки
пользовательской работынаперсональномкомпьютере, основнымпонятиямимето-
дам современногопрактическогопрограммирования. Предметомизучениякурсаяв-
ляется объектно-ориентированноепрограммирование наязыкеСи++ всредесовре-
менных 32-хразрядныхоперационныхсистемсемейства Windows. Программакурса
6
разбита на 4 части:
1) ВведениевпрограммированиенаязыкеСи++ (9 лекций)
2) Основыпрограммированиятрехмернойграфики (8 лекций)
3) Объектно-ориентированноепрограммированиенаязыкеСи++ (9 лекций)
4) Программированиедля Microsoft Windows сиспользованием Visual C++ и
библиотеки классов MFC (9 лекций)
На каждоелекционное занятиедолжнобытьпредусмотреноминимумодно
практическое (2 академическихчаса) иеще, всреднем, 4 часасамостоятельныхзаня-
тий. Т.о., наизучениекурсаотводится 72 лекционныхчаса, 72 практических (т.о., 144
аудиторных часа) и 144 часасамостоятельныхзанятий.
Методические материалыдлякурсасформированынаосновепримерно 10-ти
зарубежных изданий, частьизкоторыхпереведенанарусскийязык.
В первойчастикурсарассматриваютсяпроцедурныеосновы языка Си++. Они
включают всебяоформлениетекстапрограмм, правилазаписивыраженийнаСи++,
рассмотрение простыхтиповданныхиалгоритмическихконструкцийусловныхопе-
раторов, циклов_______идр. Вконцеэтойчастикурсаподробнорассматриваютсясоставные
типы данных. Приэтомделаютсязамечанияодостоинствахинедостаткахэтихтипов
данных, чтовпоследствииупрощаетвведениепонятийобъектно-ориентированного
программирования.
Вторая частькурсапосвященаприменениюязыкаСи++ дляпрограммирования
задач вконкретнойпредметнойобласти–трехмернойкомпьютернойграфики. Вка-
честве базовойграфическойбиблиотекивыбранабиблиотека OpenGL, являющаяся
открытым стандартомвданнойобласти. Изучениеэтойбиблиотекидемонстрирует
методику освоенияготовогоинструментария, сформированноговрезультатеприме-
нения методовструктурногопроектированиявконкретнойпредметнойобласти. По-
нятия, относящиесякмашиннойграфике, понятиекаркасаприложенияиобработки
событий иллюстрируютсяпростымипримерамиивдальнейшемиспользуютсяпри
изучении программированиявсреде Windows.
Третья частькурсаизучаетсяпослеусвоениястудентамипроцедурногопро-
граммирования. Основныевопросыобъектно-ориентированногопрограммирования
на Си++ излагаются напримерепрограммированияконсольныхприложений Win32.
Рассматриваются элементарныеприемыобъектно-ориентированногопроектирования
–проектированиенаосновераспределенияобязанностей, метод CRC-карточек.
В четвертойчастикурсаизучаетсяархитектураоперационныхсистемсемейст-
ва Windows иметодыпрограммированиядляэтихОС. Примернотретьэтойчастипо-
священа рассмотрениюосновныхкомпонентоперационныхсистем Windows 9x/NT,
знакомству сбазовыми сервисамиоперационныхсистемипрограммированиюдля
этих ОСнаязыкеСи++ науровне Win32 API. Воставшейсячастирассматриваются
приемы программированиядляОС Windows набазебиблиотекиклассов MFC. Эта
библиотека классовявляетсяпромышленнымстандартом, упрощающимразработку
программ ииспользование Win32 API. Подробноописываетсякаркасприложения
MFC, основныеклассыэтойбиблиотеки, приемыиспользованияэтихклассоввсоб-
ственных программах, архитектура однодокументныхприложений «документ/вид».
После изучениякурсастудентполучаетдостаточнополноепредставлениео
содержании современногообъектно-ориентированногопрограммирования, обуст-
ройстве современныхоперационныхсистем Win32 иособытийно-управляемомпро-
граммировании. Напрактическихзанятияхвырабатываютсянавыкипрограммирова-
ния наСи++ винтегрированнойсредеразработки Microsoft Visual C++ 5.0.
7
ЛЕКЦИЯ 1. Основы Си++
Программирование –этотехническаятворческаядеятельность, целькоторой
заключается врешенииважныхдлячеловеказадачиливыполненииопределенных
действий спомощьюкомпьютера. Нарис. 1 представленаидеализированнаясхема
решения типичнойзадачипрограммирования.
Подробное описание
задачи илинеобходимых
действий КОМПЬЮТЕР
Решение задачиили
выполнение действий
Рис. 1.
Схема решениязадачиспомощьюкомпьютера.
В рамкахтакойсхемынеобходимымикомпонентамикомпьютераявляются
центральный например, «подробноеописание (спецификация) задачи» наестественномязы-
ке длякомпьютеранегодится (внастоящеевремя). Болеетого, длярешениязадачина
компьютере недостаточнополногоописаниязадачи, необходимотакжеснабдить
компьютер информациейотом, какименноследуетрешатьзадачу–т.е. составитьал-
горитм. Дляописанияалгоритмоврешениязадачилиалгоритмоввыполнениякаких-
либо действий (например, управлениероботом-манипулятором) спомощьюкомпью-
тера применяютсяязыкипрограммирования.
На рис. 3 показанаболееподробнаясхемарешениязадачиспомощьюкомпью-
тера, вкоторойучтенанеобходимостьиспользованияязыкапрограммирования. Ил-
люстрация этойсхемынаконкретномпримереприведенавтаблице 1.
Существует большоеколичестворазличныхязыковпрограммированияимного
способов ихклассификации. например, «языкамивысокогоуровня» считаютсяте
языки, синтаксискоторыхсравнительно близоккестественномуязыку, втовремякак
синтаксис «низкоуровневых» языковсодержитмноготехническихподробностей, свя-
занных сустройствомкомпьютераипроцессора.
8
Рис. 3.
Схема решениязадачинакомпьютересиспользованиемязыкапрограммирования.
Таблица 1.
Основные этапырешениязадачипопроверкечисланапростоту.
Спецификация задачи
Требуется определить, являетсялиданноечислопростым.
Алгоритм
Описание алгоритма на
языке высокого уровня
#include <iostream.h>
int main()
{
int x;
cout << «Введите число:n»;
cin >> x;
for (int z=2; z<x; z++)
if (x % z == 0)
{
cout << «Это не простое число.n»;
return 0;
}
cout << «Это простое число.n»;
return 0;
}
Объектный код (внут-
ренний код конкретного
компьютера)
Двоичные командыпроцессора (частично)
Исполняемый файл для
конкретного компьютера
Двоичные командыпроцессора (полностью)
«Императивные» или «процедурные» языкипозволяютпрограммистуописать, в
какой последовательностикомпьютербудетвыполнятьотдельныешагиалгоритмаи,
таким образом, решатьзадачуивыдаватьнаэкранрезультат. «Декларативные» языки
предназначены большедляописаниясамойзадачиижелаемогорезультата, анедей-
ствий компьютера.
«Объектно-ориентированныеязыки» рассчитанынаприменениеособогопод-
хода кописаниюзадач, согласнокоторомувзадачевыделяютсянекоторые «объекты»
с характернымдляних «поведением» ивзаимодействующиемеждусобой. Одиниз
9
первых объектно-ориентированныхязыков–Смоллток, онпредназначенисключи-
тельно дляобъектно-ориентированногопрограммирования. Вотличиеотнего, язык
Си++ обладаеткакобъектно-ориентированнымивозможностями, такисредствами
традиционного процедурногопрограммирования.
Радикальные приверженцыразличныхязыковистилей программирования
иногда делаютэкстравагантныезаявления, выделяющиесемействоязыковилиодин
язык какисключительныйиидеальноподходящийдлялюбыхзадач. Например, до-
вольно распространеномнение, чтообъектно-ориентированныйподходнаиболее
близок кспособурешениязадаччеловеком. Поэтомуповодувысовременемсможе-
те составитьсобственноемнение, т.к. абсолютноистинного, очевидно, нет.
язык Си++ былразработанвначале 1980-хгг. БьерномСтрауструпомизком-
пании AT&T Bell Laboratories. Си++ основаннаязыкеСи. Двасимвола «++» вназва-
нии –этоиграслов, символами «++» вязыкеСиобозначаетсяоперацияинкремента
(увеличениезначенияпеременнойна 1). Т.о., Си++ был задуманкакязыкСисрас-
ширенными возможностями. БольшаячастьязыкаСивошлавСи++ какподмножест-
во, поэтомумногиепрограммынаСиможноскомпилировать (т.е. превратитьвнабор
низкоуровневых команд, которыекомпьютерможетнепосредственновыполнять) с
помощью компилятораСи++.
При классификацииязыковпрограммированияязыкСивызываетнекоторые
трудности. Посравнениюсассемблером, этовысокоуровневыйязык. ОднакоСисо-
держит многонизкоуровневых средствдлянепосредственныхоперацийспамятью
компьютера. ПоэтомуязыкСиотличноподходитдлянаписанияэффективных «сис-
темных» программ. НопрограммыдругихтиповнаСимогутоказатьсядовольно
сложными дляпонимания, иестьрядошибок, которымпрограммынаСиособенно
подвержены. Дополнительныеобъектно-ориентированныевозможностиСи++ были
добавлены вСи, вчастности, дляустраненияэтихнедостатков.
Национальный ИнститутСтандартизацииСША (American National Standards
Institution, ANSI) разработал «официальные» стандартыдлямногихязыковпрограм-
мирования, втомчиследляСииСи++. Этистандартысталиобщепринятымииони
имеют оченьбольшоезначение. Программу, целикомнаписанную на ANSI Си++, га-
рантированно можнозапуститьналюбомкомпьютере, длякоторогоимеетсякомпи-
лятор ANSI Си++. Другимисловами, стандартгарантируетпереносимостьпрограмм
на языке ANSI Си++.
В действительностибольшинствоверсийСи++ представляютсобойстандарт-
ный ANSI Си++, дополненныйнекоторымимашинно-зависимымивозможностями.
Эти специфическиесредствапредназначеныдляоблегчениявзаимодействияпро-
грамм сконкретнымиоперационнымисистемами. Вообще, впрограммах, которые
должны бытьпереносимыми, подобнымиспецифическимивозможностямиследует
пользоваться какможнореже. ВтакихслучаяхчастипрограммынаСи++, вкоторых
используются не-ANSI компонентыязыка, целесообразноособымобразомпомечать,
так, чтобыихлегкоможнобылоотделитьотосновнойчастипрограммыимодифи-
цировать длядругихкомпьютеровиоперационныхсистем.
10
Известно, чтолучшийспособизученияязыкапрограммированиязаключаетсяв
том, чтобыписатьнанемпрограммыипроверять, какониработаютнакомпьютере.
Для этогонеобходимынесколькопрограмм:
•Текстовый редактор, спомощьюкоторогоможнонабиратьиредактировать
исходный текстпрограммнаСи++.
•Компилятор. Этапрограммавыполняетпреобразованиеисходноготекстав
машинные команды, которыекомпьютерможетнепосредственновыпол-
нять.
•Компоновщик, которыйсобираетотдельныескомпилированныечастипро-
граммы вединоецелоеи, принеобходимости, добавляеткнимкомпоненты
из готовыхбиблиотек. Врезультатекомпоновкиполучаетсяготоваякза-
пуску программа–исполняемыйфайл.
•Отладчик, спомощьюкотороголегчеискатьошибкивпрограмме. Ошибки
могут обнаружитьсякакприкомпиляции, такивовремяработыпрограм-
мы.
В данномкурсеизученияСи++ практическиеупражненияпредполагаетсявы-
полнять всредеразработкипрограммMicrosoft Developer Studio Visual C++
для
IBM-совместимыхПКподуправлениемWindows 95/NT
. Вэтомпакетеинтегрирова-
ны редактор, компилятор, компоновщикиотладчик. Всевместеониобразуютединую
удобную средупрограммирования. КраткоеописаниеработысосредойVisual C++
приведено в
.
Ниже приведенисходныйтекстпростойпрограммынаСи++.
// В языке Си++ с двойной косой черты начинаются комментарии
// (например, как эта строка). Компилятор игнорирует комментарии,
// начиная от первой черты и до конца строки.
/* Второй способ записи комментариев – после косой черты со звездочкой.
После текста комментария надо поставить звездочку, а затем – косую
черту. Комментарии, записанные подобным образом, могут занимать
больше одной строки. */
/* В программе ОБЯЗАТЕЛЬНО должно быть достаточное количество
комментариев! */
/* Эта программа запрашивает у пользователя текущий год, возраст
пользователя и еще один год. Затем программа вычисляет возраст
пользователя, который будет у него во втором введенном году.*/
#include <iostream.h>
int main()
{
int year_now, age_now, another_year, another_age;
cout << «Введите текущий год и нажмите ENTER.n»;
cin >> year_now;
cout << «Введите свой возраст (в годах).n»;
cin >> age_now;
11
cout << «Введите год, для которого вы хотите узнать свой возраст.n»;
cin >> another_year;
another_age = another_year — (year_now — age_now);
if (another_age >= 0)
{
cout << «В » << another_year << » году вам будет «;
cout << another_age << «n»;
}
else
{
cout << «В » << another_year << » вы еще не родились!n»;
}
return 0;
}
программа 5.1.
Некоторые свойствапрограммы 5.1 являютсяобычнымидлябольшинствапро-
грамм наСи++. Программаначинается (послекомментариев) соператора
#include <iostream.h>
Этот операторназывается «директивойinclude». Докомпилятораисходный
текст обрабатывается
–специальнойпрограммой, котораямодифи-
цирует текстпрограммыпоспециальнымкомандам–директивам. Директивыпре-
процессора начинаютсяссимвола «#». Директиваinclude предназначена длявклю-
чения висходныйтекстсодержимогодругогофайла. например, впрограмму 5.1
включается файлiostream.h, содержащийописанияфункцийстандартнойбиблиоте-
ки ввода/выводадляработысклавиатуройиэкраном. (Стандартныебиблиотекиязы-
ка Си++ будутрассматриватьсяпозже).
Алгоритм, записанныйвпрограмме 5.1, оченьпростой. Поэтомуструктуру
программы легкопредставитьввидеспискапоследовательновыполняемыхкоманд
(операторов). Схематичнопрограмму, содержащуюсяпоследирективы#include,
можно представитьввиде:
int main()
{
;
…
…
;
return 0;
}
Подобная структураявляетсяобщейдлявсехпрограммнаСи++. Каждыйопе-
ратор втелепрограммызавершаетсяточкойсзапятой. Вхорошоразработанной
большой программебольшинствооператоровявляютсяобращениями (вызовами) к
подпрограммам, которыезаписываютсяпосле функцииmain() или вотдельных
файлах. Каждаяподпрограмма (функция) имеетструктуру, подобнуюфункции
main(). Нофункцияmain() в каждойпрограмметолькоодна. Именноснееначина-
ется выполнениепрограммы. (Подробнеефункциибудутрассматриватьсядалее.)
В концефункцииmain() записана строка:
12
return 0;
Эта строказначит «вернутьоперационнойсистемевкачествесигналаобус-
пешном завершениипрограммызначение 0″. Операторвозвратаreturn применяется
не толькопризавершениипрограммы, ноипризавершенииотдельныхподпрограмм.
В любомслучаеэтотоператорвозвращаетопределенноезначениенаболеевысокий
уровень управления.
В программе-примереиспользуются четыре
:
year_now, age_now, another_year и another_age
Переменные впрограммированииотличаютсяотматематическихпеременных.
Они используютсякаксимволические имена «фрагментовоперативнойпамятиком-
пьютера». Привыполнениипрограммывразличныемоментывременипеременные
могут хранитьразличныезначения. Впрограмме 5.1 первоеупоминаниечетырехпе-
ременных содержитсявстрокесоператоромописанияпеременных:
int year_now, age_now, another_year, another_age;
Этот операторуведомляеткомпилятор, что дляхранениячетырехпеременных
типа «целоечисло» (integer –int) требуетсявыделитьнеобходимоеколичествопамя-
ти. Этаобластьпамятибудетзарезервированавтечениевыполненияоставшейсячас-
ти программы. Переменныевсегдадолжныбытьописаныдопервогоиспользования.
В программированиихорошимстилемсчитаетсяописаниевсехпеременных, исполь-
зуемых вподпрограмме, вначалеэтойподпрограммы. ВСи++ естьнесколькораз-
личных типовпеременных, ионибудутобсуждатьсянемногопозже.
После
программы ееможно
. Результат
выполнения наэкранебудетвыглядетьпримернотак:
Введите текущий год и нажмите ENTER.
2000
Введите свой возраст (в годах).
21
Введите год, для которого вы хотите узнать свой возраст.
2017
В 2017 году вам будет 38
Первая, третья, пятаяиседьмаястрокивыдаютсянаэкранпрограммойспо-
мощью следующегооператора:
cout <<
<<
<< … <<
;
Этот операторвыводитнаэкрансообщение:
1
2
N
Последовательность операторов
cout <<
1;
cout <<
2;
…
…
cout <<
N;
13
приводит каналогичномурезультату. Еслимеждувыражениямитребуетсявставить
пробелы илиновыестроки, тоихнужноуказатьявно, спомощьюсимволов» » и
«n» соответственно.
Числа, показанныевышевпримеревыдачинаэкранполужирным
шрифтом, бы-
ли напечатаныпользователем. Впоказанномпримере оператор
cin >> year_now;
приводит ктому, чтопеременнойyear_now
2000
. Этопроис-
ходит послетого, какпользовательнапечатает «2000
» инажметклавишуEnter
. В
программе естьещеместа, гдепеременнымприсваиваютсязначения, втом числе
оператор присваивания:
another_age = another_year — (year_now — age_now);
Операция «=» означает «присвоитьпеременной, стоящейслеваотзнакаравен-
ства,
ным символом: «==».
В несколькихпоследнихстрокахпрограммы (достроки «return 0») записано:
if (another_age >= 0)
{
cout << «В » << another_year << » году вам будет «;
cout << another_age << «n»;
}
else
{
cout << «В » << another_year << » вы еще не родились!n»;
}
Оператор ветвления (условныйоператор) «if…else…» выглядитпримерно
одинаково вовсехпроцедурныхязыкахпрограммирования. ВСи++ онназывается
просто
, иегообщаяструктуратакова:
if ( условие )
{
1;
…
…
N;
}
else
{
N+1;
…
…
N+M;
}
часть «else (иначе)» воператореif необязательна. Болеетого, еслипосле
«if ( условие )» стоиттолькоодиноператор, томожноопуститьфигурныескобкии
записать оператортак:
14
if ( условие )
1;
В программахусловныеоператорычастовстречаютсягруппами, например:
…
…
if (total_test_score < 50)
cout << «Вы не прошли тест. Выучите материал как следует.n»;
else if (total_test_score < 65)
cout << «Вы прошли тест со средним результатом.n»;
else if (total_test_score < 80)
cout << «Вы хорошо выполнили тест.n»;
else if (total_test_score < 95)
cout << «Вы показали отличный результат.n»;
else
{
cout << «Вы сдали тест нечестно!n»;
total_test_score = 0;
}
…
…
Приведенный фрагментпрограммыможетпоказатьсядовольносложным. Тем
не менее, онсоответствуетправиламСи++. Этолегкопонять, еслиобратитьсяксин-
таксической диаграммеоператораif (рис. 4).
В овальныхиликруговыхрамкахнасинтаксическихдиаграммахуказываются
элементы языка, которыебуквальнотакивоспроизводятсявисходномтекстепро-
грамм. Впрямоугольныхрамкахприведеныэлементы, требующиедальнейшегооп-
ределения, возможно, спомощьюдругихсинтаксическихдиаграмм. Набортакихдиа-
грамм служитформальнымописаниемсинтаксисаязыкапрограммирования.
Обратите внимание, чтонарис. 4 отсутствуетсимвол «;» иразделители «{}».
Эти элементыязыкавключенывопределение (исинтаксическуюдиаграмму) для
обобщенного понятия «операторязыкаСи++».
Рис. 4.
Синтаксическая диаграммаоператораif.
При обработкеприведенногофрагментапрограммыкомпиляторСи++ трактует
весь текст, выделенныйнижеполужирнымшрифтом, какодиноператорпослеперво-
го словаelse.
…
…
if (total_test_score < 50)
cout << «Вы не прошли тест. Выучите материал как следует.n»;
else if (total_test_score < 65)
cout << «Вы прошли тест со средним результатом.n»;
else if (total_test_score < 80)
cout << «Вы хорошо выполнили тест.n»;
15
else if (total_test_score < 95)
cout << «Вы показали отличный результат.n»;
else
{
cout << «Вы сдали тест нечестно!n»;
total_test_score = 0;
}
…
…
Между текстомпрограммы, приведеннымвп.5 итекстом, которыйпоказан
ниже, длякомпилятораСи++ нетникакихразличий.
#include <iostream.h> int main() { int year_now, age_now, another_year,
another_age; cout << «Введите текущий год и нажмите ENTER.n»; cin >>
year_now; cout << «Введите свой возраст (в годах).n»; cin >> age_now;
cout << «Введите год, для которого вы хотите узнать свой возраст.n»; cin
>> another_year; another_age = another_year — (year_now — age_now); if
(another_age >= 0) { cout << «В » << another_year << » году вам будет «;
cout << another_age << «n»; } else { cout << «В » << another_year << «
вы еще не родились!n»; } return 0; }
Отсутствие
,
,
и
делают эту
программу практическинепригоднойдлячтениячеловеком. Длявыработкихорошего
стиля программирования, конечно, требуется знатьнетолькоправилаоформления
текста программы, ноихследуетсоблюдатьссамогоначала. Приоформлениисобст-
венных программбудьтепоследовательныиделайтетак, чтобыотступыипробелы
отражали логическуюструктурувашихпрограмм.
Для переменныхследуетвыбиратьосмысленныеимена: имена «year_now»,
«age_now», «another_year» и «another__age» лучше, чем «y_n», «a_n», «a_y» и
«a_a» инамноголучше, чем «w», «x», «y» и «z». Этоособенноважно, есливбудущем
ваши программымогутпотребоватьизмененияспомощьюдругихпрограммистов.
В даннойлекциикраткоинеформальнобылирассмотренынескольковажных
вопросов: переменныеитипыданных, вводивывод, оператор присваиванияиуслов-
ный оператор («операторif»). Болеестрогоиподробноэтивопросыбудутрассмот-
рены впоследующихлекциях.
Для выполненияэтихупражненийтребуетсянекоторыйопытработысПКпод
управлением операционнойсистемыWindows 95/NT
.
Упражнение 1
Изучите краткоеруководствопоVisual C++
в
. Создайтепроектс
именем «AgeCalculator». СоздайтеисходныйфайлсименемAgeCalculator.cpp
16
и наберитевнемисходныйтекстпрограммы 5.1. Сохранитефайлнадискеидобавьте
его впроект. Соберитепроектизапуститепрограммунавыполнение.
Возможно, вывстретитесьсоследующимипроблемами:
Эта проблемаобъясняетсяразличиемтаблицкодировокWindows
и DOS
. В этих таблицах
русские буквырасположенывразныхместах. Консольныепрограммыприработеисполь-
зуют кодировкуDOS
, атекстовыйредакторVisual C++
–кодировкуWindows
. Поэтому
вам придетсядобавитьпреобразованиестроксрусскимибуквамиизкодировкиWindows
в кодировкуDOS
.
Для этоговключитевпрограмму, послефайлаiostream.h, файлwindows.h с описа-
нием функцийоперационнойсистемыWindows
:
#include <windows.h>
Перед функциейmain() создайте новуюфункциюсименем rus_str(), котораябудетвы-
полнять необходимоепреобразованиеспомощьюспециальнойфункцииWindows
:
char* rus_str( char* str )
{
CharToOem( str, str );
return str;
}
Во всехстрокахпрограммы, гденаэкранвыдаютсясимвольныестрокисрусскимибук-
вами, укажитепреобразованиеэтихстрокспомощьюновойфункции, например:
cout << rus_str( «Введите текущий год и нажмите ENTER.n» );
Для исправленияэтогонедостаткапрощевсегопредусмотретьвконцепрограммыввод
произвольного символа. Покапользовательненажметкакую-нибудьсимвольнуюклави-
шу ипотомEnter
, окнопрограммыбудетоставатьсянаэкране. Дляэтогопотребуетсяза-
вести символьнуюпеременную (строкусописаниемэтойпеременнойрасположитепосле
строки сописаниемцелочисленныхпеременных):
char wait_char;
Перед строкойсоператоромвозврата «return 0» добавьтеоператордлявводасимвола
с клавиатуры:
cin >> wait_char;
Сравните результатыработысвоейпрограммыспримеромизлекции. Поэкс-
периментируйте надулучшениемилиизменениемформатавыводанаэкран.
Упражнение 2
Модифицируйте программу 5.1, чтобыприпревышениипеременной
«another_age» значения150
на экранвыводилосьсообщение:
Извините, но вы вряд ли доживете до
года!
Проверьте работусвоейпрограммыдлянесколькихразныхлет.
Упражнение 3
Измените программуизупр.2 так, чтобывнейучитывалисьигоды, имесяцы.
На экранпрограммадолжнавыводитьследующиесообщения:
Введите текущий год и нажмите ENTER.
2000
Введите текущий месяц (число от 1 до 12).
10
17
Введите свой возраст (в годах).
25
Введите месяц своего рождения (число от 1 до 12).
5
Введите год, для которого вы хотите узнать свой возраст.
2006
Введите месяц этого года.
6
Ваш возраст в 6/2006: 31 год и 1 месяц.
Программа должнавыдаватькорректныесообщениядляединственногоимно-
жественного числалетимесяцев, т.е. должнавыводитьнаэкран «25 лет и 1 ме-
сяц», но «24 года и 2 месяца».
Символ Операция Пример
+
Сложение 3 + 5 8
—
Вычитание 43 — 25 18
*
Умножение 4 * 7 28
/
Деление 9/2 4
%
Остаток приделе-
нии нацело
20 % 6 2
Символ Операция Пример
<
меньше, чем 3 < 5 TRUE (Истина)
<=
меньше илиравно 43 <= 25 FALSE (ложь)
>
больше, чем 4 > 7 FALSE
>=
больше илиравно 9 >= 2 TRUE
==
равно 20 == 6 FALSE
!=
не равно 20 != 6 TRUE
&&
Логическое И 5 > 2 && 6 > 10 FALSE
||
Логическое ИЛИ 5 > 2 || 6 > 10 TRUE
18
ЛЕКЦИЯ 2. Переменные, типы данных и выражения
В исходномтекстепрограммнаСи++ используетсядовольномногоанглий-
ских словиихсокращений. Всеслова (идентификаторы), встречающиесявпрограм-
мах, можноразделитьнатрикатегории:
1) Служебные слова языка.
Например, этословаif, int и else. Назначение
этих словпредопределеноиегонельзя изменить. Нижеприведенболее
полный списокслужебныхслов:
asm continue float new signed try
auto default for operator sizeof typedef
break delete friend private static union
case do goto protected struct unsigned
catch double if public switch virtual
char else inline register template void
class enum int return this volatile
const extern long short throw while
По назначениюэтисловаможноразбитьнаотдельныегруппы (прил. 8.1).
2) Библиотечные идентификаторы.
Назначение этихсловзависитотсреды
программирования. Вслучаесерьезнойнеобходимостипрограммистможет
изменить ихсмысл. Примерытакихслов: cin, cout и sqrt (имяфункции
извлечения квадратногокорня).
3) Идентификаторы, введенные программистом.
Эти слова «создаются»
программистом –например, именапеременных (такие, какyear_now и another_
age в программе 1.5.1).
Идентификатором не можетбытьпроизвольнаяпоследовательностьсимволов.
По правиламСи++, идентификаторначинаетсясбуквыилисимволаподчеркивания
(«_») исостоиттолькоизанглийскихбукв, цифрисимволовподчеркивания.
2.1 Целые числа
Правила Си++ требуют, чтобывпрограммеувсехпеременныхбылзадантип
данных. Типданныхint встречался намуженеоднократно. Переменныеэтоготипа
применяются дляхраненияцелыхчисел (integer). Описаниепеременной, какимею-
щей типint, сообщаеткомпилятору, чтоондолженсвязатьсидентификатором
(именем) переменнойколичествопамяти, достаточноедляхраненияцелогочиславо
время выполненияпрограммы.
Границы диапазонацелыхчисел, которыеможнохранитьвпеременныхтипа
int, зависятотконкретногокомпьютера. ВСи++ естьещедвацелочисленныхтипа–
short int и long int. Онипредставляют, соответственно, болееузкийиболее
широкий диапазонцелыхчисел, чемтипint. Добавлениеклюбомуизэтихтипов
префикса unsigned означает, чтовпеременной будутхранитсятольконеотрица-
тельные числа. например, описание:
unsigned short int year_now, age_now, another_year, another_age;
19
резервирует памятьдляхранениячетырехотносительнонебольшихнеотрицательных
чисел.
Приведем несколькополезныхправил, касающихсязаписицелочисленных
значений висходномтекстепрограмм.
1) Нельзяпользоватьсядесятичнойточкой. значения 26 и 26.0 одинаковы, но
«26.0» неявляетсязначениемтипа «int».
2) Нельзяпользоватьсязапятымивкачестверазделителейтысяч. например,
число 23,897 следуетзаписывать как «23897».
3) Целыезначениянедолжныначинатьсяснезначащегонуля. Онприменяется
для обозначенияустаревшихвосьмеричныхчисел, такчтокомпиляторбу-
дет рассматриватьзначение «011» какчисло 9 ввосьмеричнойформе.
2.2 Вещественные числа
Для хранениявещественныхчиселприменяютсятипыданныхfloat и
double. Смысл знаков «+» и «-» длявещественныхтиповсовпадаетсцелыми. По-
следние незначащиенулисправаотдесятичнойточкиигнорируются. Поэтомувари-
анты записи «+523.5», «523.5» и «523.500» представляютодноитожезначение. В
Си++ такжедопускаетсязаписьвформате
(вэкспоненциальном
формате) ввиде
и
. например, 523.5 можнозаписатьввиде
«5.235e+02» (т.е. 5.235*10*10), а -0.0034 ввиде «-3.4e-03».
В большинствеслучаевиспользуетсятипdouble, онобеспечиваетболеевысо-
кую точность, чемfloat. Максимальнуюточностьинаибольшийдиапазончисел
достигается спомощьютипаlong double, ноонтребуетбольшепамяти (в
Visual C++
10 байтначисло), чем double (8 байт).
2.3 Преобразование типов в выражениях
При выполнениивычисленийиногдабываетнужногарантировать, чтоопреде-
ленное значениебудетрассматриватьсякаквещественноечисло, дажееслинасамом
деле это целое. Чащевсегоэтонужноприделенииварифметическихвыражениях.
Применительно кдвумзначениямтипаint операция деления «/» означаетделение
нацело, например, 7/2 равно 3. Вданномслучае, еслинеобходимополучитьрезультат
3.5, томожнопростодобавитьдесятичнуюточкувзаписиодногоилиобоихчисел:
«7.0/2», «7/2.0» или «7.0/2.0». Ноеслиивчислителе, ивзнаменателестоятперемен-
ные, анеконстанты, тоуказанныйспособнеподходит. Вместонегоможноприме-
нить явноепреобразованиетипа. например, значение «7» преобразуетсявзначение
типа double с помощьювыражения «double(7)». Поэтомуввыражении
answer = double(numerator) / denominator
операция «/» всегдабудетрассматриватьсякомпиляторомкаквещественноеделение,
даже если «numerator» и «denumerator» являютсяцелымичислами. Дляявногопреоб-
разования типовможнопользоватьсяидругимиименамитиповданных. например,
«int(14.35)» приведеткполучениюцелогочисла14.
20
2.4 Символьный тип
Для хранениясимвольныхданныхвСи++ предназначентип «char». Перемен-
ная типа «char» рассчитананахранениетолькоодногосимвола (например, буквы
или пробела). Впамятикомпьютерасимволыхранятсяввидецелыхчисел. Соответ-
ствие междусимволамииихкодамиопределяетсятаблицейкодировки, котораязави-
сит откомпьютераиоперационнойсистемы. Почтивовсехтаблицахкодировкиесть
прописные истрочныебуквыанглийскогоалфавита, цифры0,…,9, инекоторые
специальные символы, например, #, ., !, +, — и др. Самойраспространеннойтаблицей
кодировки, скореевсего, является таблицасимволов ASCII.
В текстепрограммсимвольныеконстантытипа «char» надозаключатьводи-
ночные кавычки, иначекомпиляторпойметихнеправильноиэтоможетпривестик
ошибке компиляции, или, чтоещехуже, кошибкамвременивыполнения. например,
«‘A'» являетсясимвольнойконстантой, но «A» будетрассматриватьсякомпиляторомв
качестве именипеременной. Аналогично, «‘9′» являетсясимволом, а «9» –целочис-
ленной _______константой.
Т.к. впамятикомпьютерасимволыхранятсяввидецелыхчисел, тотип «char»
на самомделеявляетсяподмножествомтипа «int». НаСи++ разрешаетсяиспользо-
вать символыварифметическихвыражениях. например, налюбомкомпьютерес
таблицей ASCII следующеевыражениедастистинноезначение (TRUE, или1):
‘9’-‘0’ == 57-48 == 9
В таблице ASCII кодомсимвола ‘9’ являетсядесятичноечисло 57 (вшестнадца-
теричной записи 0x39), а ASCII–код символа ‘0’ равендесятичномучислу 48 (шестна-
дцатеричное
57-48 == 0x39-0x30 == 9
Кодами ASCII удобнеепользоватьсявшестнадцатеричнойформе. Призаписи
шестнадцатеричных чиселвСи++ применяетсядвухсимвольныйпрефикс «0x».
Переменные типа «char» существенноотличаютсяот «int» привыполнении
ввода данныхсклавиатурыивыводанаэкран. Рассмотримследующуюпрограмму.
#include <iostream.h>
int main()
{
int number;
char character;
cout << «Напечатайте символ и нажмите Enter:n»;
cin >> character;
number = character;
cout << «Вы ввели символ ‘» << character;
cout << «‘.n»;
cout << «В памяти компьютера он хранится в виде числа «;
cout << number << «.n»;
return 0;
}
программа 2.1.
Программа 2.1 выдаетнаэкранследующиесообщения:
21
Напечатайте символ и нажмите Enter:
9
Вы ввели символ ‘9’.
В памяти компьютера он хранится в виде числа 57.
Программу 2.1 можноизменитьтак, чтобыонапечаталавсютаблицусимволов
ASCII. Дляэтогопридетсяприменить «операторциклаfor». «Циклfor» является
примером
–этиоператорыбудутрассмотреныподробноводнойиз
следующих лекций. Операторfor имеет следующийсинтаксис:
for (
;
;
)
{
1;
…
…
N;
}
Цикл for выполняется вследующемпорядке: (1) Сначалавыполняетсяопера-
тор
. (2) Выполняетсяпроверка, являетсяли
истин-
ным. Еслиусловиеложно, тооператор for завершается. Еслиусловиеистинно, то
последовательно выполняютсяоператорытелацикла
1…
N, изатем
выполняется оператор
. Послеэтогопроисходитпереходнанача-
ло шага (2).
Чтобы кодсимволавывестинаэкранвшестнадцатеричнойформе, надоснача-
ла послатьнаэкранслужебный символ-манипулятор. Программадляпечатифраг-
мента таблицы ASCII (от 32-госимвола (пробел) до 126-го (символ ‘~’)), будетвыгля-
деть так:
#include <iostream.h>
int main()
{
int number;
char character;
for (number = 32; number <= 126; number = number + 1 )
{
character = number;
cout << «Символ ‘» << character;
cout << «‘ имеет код «;
cout << dec << number << » (дес.) или «;
cout << hex << number << » (шестнд.).n»;
}
return 0;
}
программа 2.2.
Программа 2.2 напечатаетнаэкране:
Символ ‘ ‘ имеет код 32 (дес.) или 20 (шестнд.).
Символ ‘!’ имеет код 33 (дес.) или 21 (шестнд.).
…
…
Символ ‘}’ имеет код 125 (дес.) или 7D (шестнд.).
Символ ‘~’ имеет код 126 (дес.) или 7E (шестнд.).
22
2.5 Символьные строки
В большинстверассмотренныхпримеровпрограммдлявыводанаэкранчасто
используются символьныестроки. ВСи++ символьныестрокизаключаютсявдвой-
ные кавычки. Поэтомувпрограммахчастовстречаются операторывыводавроде:
cout << «‘ имеет код «;
На самомделевСи++ строковыйтип («string») неявляетсястандартнымти-
пом данных, таким, как, например, «int», «float» или «char». Строкихранятсявпамя-
ти ввидесимвольныхмассивов, поэтомустрокибудутрассматриватьсяпозднее, при
изучении массивов.
2.6 Типы данных, определяемые пользователем
вопрос отипахданных, определяемыхпользователем, будетобсуждатьсяна-
много болееподробновпоследующихлекциях. Будетпоказано, какпрограммист
может определитьсобственныйтипданных, необходимыйдлярешенияконкретной
задачи. Средстваопределенияновыхтиповданных–однаизнаиболеемощныхвоз-
можностей Си++, которыепозволяютхранитьиобрабатыватьвпрограммахнаСи++
сложные структурыданных.
При выводенаэкранчисленныхзначенийтипа «float», «double» или «long
double» возможноуказаниеточностипредставленияданныхнаэкранеизаданиене-
которых дополнительныхпараметровотображения, например, отображениезначений
в форматесфиксированнойилиплавающейточкой.
В программе 3.1 вещественноечислоотображаетсявформатесфиксированной
точкой идвумядесятичнымизнакамипослезапятой. Идентификатор «sqrt» является
именем библиотечнойфункцииизвлеченияквадратногокорня. Описаниебиблиотеки
математических функций содержитсявзаголовочномфайле «math.h».
#include <iostream.h>
#include <math.h>
int main()
{
float number;
cout << «Введите вещественное число.n»;
cin >> number;
cout << «Корень из «;
cout.setf(ios::fixed); // СТРОКА 12
cout.precision(2);
cout << number;
cout << » примерно равен » << sqrt( number ) << «.n»;
return 0;
}
программа 3.1.
Программа 3.1 напечатаетнаэкране:
23
Введите вещественное число.
200
Корень из 200.00 примерно равен 14.14.
Если СТРОКУ 12 заменитьна «cout.setf(ios::scientific);», товидрезульта-
та изменится:
Введите вещественное число.
200
Корень из 2.00e+02 примерно равен 1.41e+01.
В выходныеданныеможновключитьпараметрытабуляции. Дляэтогопредна-
значена функцияшириныполя, например, «cout.width(20)». Оназадаетширинусле-
дующего выводимогонаэкранзначенияравной, какминимум, 20 символам (при
меньшей ширинеавтоматическибудутдобавленыпробелы). Этавозможностьосо-
бенно полезнадляпечатитаблиц.
В компилятореVisual C++
при указаниишириныполяпоумолчаниюпредпо-
лагается, чтозначениявыравниваютсяпоправойгранице. Чтобызадатьвыравнива-
ние полевойграницеполя, потребуетсяиспользоватьещенесколькоманипуляторов
ввода-вывода. Этоспециальныефункциииоператоры, содержащиесявбиблиотеке
ввода/выводаСи++. Ониописанывзаголовочномфайлеiomanip.h. Для заданиявы-
равнивания полевойграниценадоустановитьспециальныйфлажок (переключатель)
с помощьюфункцииsetiosflags:
#include <iostream.h>
#include <iomanip.h>
#include <math.h>
int main()
{
int number;
cout << setiosflags( ios::left );
cout.width(20);
cout << «Число» << «Квадратный кореньnn»;
cout.setf( ios::fixed );
cout.precision(2);
for ( number = 1 ; number <= 10 ; number = number + 1 )
{
cout.width(20);
cout << number << sqrt(number) << «n»;
}
return 0;
}
программа 3.2.
Программа 3.2 выдастнаэкранследующиесообщения:
Число Квадратный корень
1 1.00
2 1.41
3 1.73
4 2.00
5 2.24
6 2.45
24
7 2.65
8 2.83
9 3.00
10 3.16
(
: вовсехпримерахидентификатор «cout» являетсяименемперемен-
ной-объектакласса «stream» (поток). Функции «setf(…)», «precision(…)» и
«width(…)» являютсяфункциями-членами класса «stream». Понятия «объект«,
«класс», «функция-член» идр. будутподробнорассматриватьсявкурсеобъектно-
ориентированного программирования.)
Как выужезнаете, впрограммахнаСи++ переменныеобязательнодолжны
быть описаныдопервогоиспользования, например, так:
float number;
После оператораописаниядомоментавыполненияпервогооператорапри-
сваивания значениепеременной «number» будетнеопределенным, т.е. этапеременная
может иметьслучайноезначение. ВСи++ можно (ижелательно) инициализировать
переменные конкретнымизначенияминепосредственноприописаниипеременных.
например, возможенследующийоператорописаниясинициализацией:
float PI = 3.1416;
Если значениепеременнойвпрограмменикогданеизменяется, тоеецелесооб-
разно защититьотслучайногоизмененияспомощьюслужебногослова «const» –т.е.,
превратить вконстанту.
4.1 Тип «Перечисление»
Для описаниянаборасвязанныхпосмыслуконстанттипа «int» вСи++ есть
оператор перечисления. например, описаниевида
enum { MON, TUES, WED, THURS, FRI, SAT, SUN };
эквивалентно описанию 7 констант-кодовднейнедели:
const int MON = 0;
const int TUES = 1;
const int WED = 2;
const int THURS = 3;
const int FRI = 4;
const int SAT = 5;
const int SUN = 6;
По умолчаниючленамперечисления «enum» присваиваютсязначения 0, 1, 2, и
т.д.. Принеобходимостичленыперечисленияможноинициализироватьдругимизна-
чениями. Неинициализированнымявночленамбудутприсвоенызначенияпопоряд-
ку, начинаяотпредыдущегопроинициализированногочлена:
enum { MON = 1, TUES, WED, THURS, FRI, SAT = -1, SUN };
В приведенномпримере «FRI» имеетзначение 5, а «SUN» –значение 0.
25
4.2 Расположение описаний констант и переменных в исходном тексте
В исходномтекстеописанияконстантчащевсегоразмещаютсявзаголовке
программы передфункцией «main». Послених, ужевтелефункции «main», размеща-
ются описанияпеременных. Дляиллюстрацииэтогопорядканижеприведенфраг-
мент программы, котораярисуетнаэкранеокружностьзаданногорадиусаивычисля-
ет еедлину (набиратьэтотпримерненадо, посколькуонприведеннеполностью.)
#include <iostream.h>
const float PI = 3.1416;
const float SCREEN_WIDTH = 317.24;
int drawCircle(float diameter); /* Это «прототип функции» */
int main()
{
float radius = 0;
cout << «Введите радиус окружности.n»;
cin >> radius;
drawCircle(radius*2);
cout.setf(ios::fixed);
cout.precision(2);
cout << «Длина окружности радиуса » << radius;
cout << » примерно равна » << 2*PI*radius << «.n»;
return 0;
}
int drawCircle(float diameter)
{
float radius = 0;
if (diameter > SCREEN_WIDTH)
radius = SCREEN_WIDTH/2.0;
else
radius = diameter/2.0;
…
…
}
После определенияфункции «main()» вэтойпрограммесодержитсяопределе-
ние функциирисованияокружности «drawCircle(…)». Деталиреализацииэтой
функции сейчаснесущественны (будемсчитать, чтофункцияdrawCircle(…)» реа-
лизована корректноиеюможнопользоватьсятакже, как, например, функцией
«sqrt(…)»). Обратитевнимание, что, хотяпеременная «radius» используетсявобеих
функциях «main()» и «drawCircle(…)», этонеоднаитажепеременная, адве
.
Если быпеременная «radius» былаописанадофункции «main», товтакомслу-
чае онабылабы
(общедоступной). Тогда, предполагая, что
внутри функции «drawCircle(…)» описанияпеременнойуженет, если «drawCircle(…)»
присвоит глобальнойпеременнойзначение «SCREEN_WIDTH/2.0», тоэто
и получитсяневерныйрезультат.
В приведеннойпрограммеглобальнойпеременнойнет, аестьдве
переменных «radius». например, перваяпеременная «radius» является
пе-
26
ременной функции «main()», или, говорят, чтофункция «main()» является
этой переменной.
Константы общегоназначения, такие, как «PI» и «SCREEN_WIDTH», принятоопи-
сывать
, чтобыонибылидоступнывнутрилюбойфункции.
Для контролядействийпрограммывприведенномфрагментепредусмотрено
повторное отображениеданных, введенныхпользователем. Другимисловами, задан-
ное пользователемзначение «radius» ещеразпечатаетсянаэкранепередотображени-
ем длиныокружности.
5.1 Краткая форма записи операторов присваивания
В программахчастовстречаютсяоператорыприсваивания, вкоторыхсправа
стоит выражение, модифицирующеетекущеезначениепеременной, например:
number = number + 1;
Переменным частоприсваиваютсязначения, вычисленныенаосновеихстарых
значений. ПоэтомувСи++ былавведена краткаяформазаписидляподобныхопера-
торов присваивания. Любуюизопераций «+» (сложение), «-» (вычитание), «*» (умно-
жение), «/» (деление) и «%» (остатокотделениянацело) можноуказатьвкачестве
префикса оператораприсваивания («=») (cм. следующуютаблицу).
Пример:
number += 1;
total -= discount;
bonus *= 2;
time /= rush_factor;
change %= 100;
amount *= count1 + count2;
Эквивалентное выражение:
number = number + 1;
total = total — discount;
bonus = bonus * 2;
time = time / rush_factor;
change = change % 100;
amount = amount * (count1 + count2);
Первый примердопускаетещеболеекраткуюзаписьспомощью оператораин-
кремента «++»:
number++;
Оператор «++» существуетивпрефикснойформе:
++number;
Постфиксная ипрефикснаяформазаписиимеютважноеразличие, котороене-
обходимо помнить. ПрефиксныйоператорприменяетсяДО
вычисления остальной
части выражения, апостфиксный–ПОСЛЕ
. Например, посолевыполненияоперато-
ров
x = 4;
y = x++;
переменная «x» получитзначение 5, а «y» –значение 4. Вслучаеоператоров
x = 4;
y = ++x;
обе переменныеполучатзначение 5. Этообъясняетсятем, что «++x» выполняетсядо
того, какзначение «x» будетиспользовановвыражении, а «x++» –после. ВСи++ су-
ществует аналогичныйоператордекремента «—«, уменьшающийзначениеперемен-
ной на 1, иунеготожеестьпрефикснаяипостфикснаяформа.
27
Вообще, выражениесоператоромприсваиванияимеетзначение, равноезначе-
нию левойчастипослевыполненияприсваивания. Нижеприведеновыражение, соот-
ветствующее правиламСи++, котороеможноиспользоватьдляпроверкиусловия:
(y = ++x) == 5
Это выражениеозначаетследующее: «послеприсвоенияпеременнойy инкре-
ментированного значенияx проверить, неравнолизначениеy числу 5″.
5.2 Логические выражения и операторы
Интуитивно логическиевыражениянаподобие «2<7», «1.2!=3.7» и «6>=9» вос-
принимаются человекомкакутверждения, которыемогутбыть «истинными (true)»
или «ложными (false)» (операция «!=» означает «неравно»). Допускаетсяобъеди-
нение несколькихподобныхвыраженийвболеесложноевыражениеспомощью ло-
гических операций «&&» («И»), «||» («ИЛИ») и «!» («НЕ») (см. таблицу).
Выражение:
(6 <= 6) && (5 < 3)
(6 <= 6) || (5 < 3)
(5 != 6)
(5 < 3) && (6 <= 6) || (5 != 6)
(5 < 3) && ((6 <= 6) || (5 != 6))
!((5 < 3) && ((6 <= 6) || (5 != 6)))
Истинно или ложно:
false
true
true
true
false
true
В таблицевчетвертомпримеревыражениеистинно, посколькуприоритетопе-
рации «&&» выше, чему «||». Приоритет (порядоквыполнения) различныхопераций
Си++ можноузнатьвучебникеилируководствепоязыкуСи++, атакжевсправочной
системе Visual C++
(тема
). Еслиувасвозникаютсомненияотно-
сительно приоритетаопераций, применяйтекруглыескобки(). Применениеэтих
скобок облегчаетчтениепрограмм.
Составные логическиевыраженияобычноприменяютсявкачествеусловийв
операторах if и вциклахfor. например:
…
…
if ( total_test_score >= 50 && total_test_score < 65 )
cout << «Вы прошли тест со средним результатом.n»;
…
…
У логических выраженийвСи++ естьещеодноважноесвойство. ВСи++ ис-
тинное
пиляторов любоеположительноечислосчитаютистиннымзначением), аложное
например,
легко напечатать «=» вместо «==». Поэтомуфрагмент
…
…
if ( number_of_people = 1 )
cout << «Есть только один человек.n»;
…
…
всегда будетпечататьсообщение «Естьтолькоодинчеловек», дажееслидооператора
if переменная «number_of_people» былабольше 1.
28
В даннойлекциидовольноподробнорассматривалисьпеременныеязыкаСи++.
У переменныхвсегдаестьопределенныйтипданных. Переменныеприменяютсядля
временного илипостоянногохранениязначенийразныхтипов. Значенияпеременным
можно присваиватьразличнымиспособами. Ввыраженияхдлявычисленияновых
значений переменныхможно использоватьразличныеарифметическиеилогические
операции.
Упражнение 1
Для преобразованиятемпературыизшкалыЦельсиявабсолютнуюшкалутем-
ператур (шкалуКельвина) надо добавить ктемпературепоЦельсиюзначение 273.15.
В шкалуФаренгейтатемпературапоЦельсиюпреобразуется
= 1.8
o + 32
.
Напишите программупреобразованиязначенийтемпературы, котораябудет
печатать наэкранеследующуютаблицу:
Цельсий Фаренгейт Абсолютная температура
0 32.00 273.15
20 68.00 293.15
40 104.00 313.15
… … …
… … …
300 572.00 573.15
Упражнение 2
Измените программуизупражнения 1 так, чтобыоназапрашивалаупользова-
теля минимальную имаксимальнуютемпературупоЦельсию, которыедолжныбыть
в первойипоследнейстрокахтаблицы. Программатакжедолжназапроситьшагиз-
менения температуры (наэтозначениедолжныотличатьсятемпературывсоседних
строках таблицы, вупражнении 1 шагбылравен 20-тиградусам).
Перед таблицейпрограммадолжнавывестинесколькострокспояснениемсво-
их действий, атакжеповторитьвыводнаэкранвведенныхпользователемданных.
Упражнение 3
Напишите программу, котораясчитываетсклавиатурысимвол (ch) изатемвы-
водит одноизследующихсообщений (вместо ch долженвыводитьсявведенныйсим-
вол, авместо … –соответствующаяпрописнаяилистрочнаябуква):
а) еслисимвол ch является 2ьстрочнойбуквой–сообщение «Букве ch соответст-
вует прописнаябуква …»,
б) если ch являетсяпрописнойбуквой–сообщение «Букве ch соответствует
строчная буква …»,
в) если ch неявляетсябуквой–сообщение «Символ ch неявляетсябуквой».
Для составлениянеобходимыхусловийобратитеськрасширеннойтаблице
символов ASCII (см. п.8.3).
Упражнение 4
Напишите программудлявозведенияпроизвольногочислаx в положительную
степень n с помощьюциклаfor. (Естьлиспособыповышенияэффективностивашей
программы?)
29
8.1 Служебные слова Си++
По назначениюслужебныесловаязыкаСи++ можноразделитьнанесколько
групп. Нижеперечисленыэтигруппыиотносящиесякнимслова. Полужирным
шрифтом выделеныслова, назначениекоторыхвыузнаетевданномвводномкурсе.
•Типы данных (определяюттипыданных, которыеможнохранитьвпамяти
компьютера).
char short int long
(целые числа)
enum
(тип «перечисление»)
double float
(вещественные числа)
void
struct
union typedef
(типы, определяемые
пользователем)
•Модификаторы типовданных (позволяютзадатьнекоторыесвойствахране-
ния данныхвпамяти).
signed unsigned
volatile register
const static extern
auto
•Управление порядкомвыполненияоператоров.
if else
(ветвление с двумя вариантами)
switch case default
(множественное ветвление)
for while do
(циклы)
break continue
return
(возврат из функции)
goto (безусловный переход)
•Динамическое распределениепамяти.
new delete
•Объектно-ориентированноепрограммирование (этисловабудутподробно
рассматриваться вотдельномкурсеобъектно-ориентированногопрограм-
мирования ипроектирования).
class private protected public
virtual this friend template
operator
•Обработка исключений (особыймеханизмобработкиошибоквобъектно-
ориентированных программах).
try throw catch
•Разное.
sizeof
inline asm
30
8.2 Таблица символов ASCII
8.3 Расширенная таблица символов ASCII для кодовой страницы DOS-866
31
ЛЕКЦИЯ 3. Функции и процедурная абстракция
Естественный иинтуитивнопонятныйподходкрешениюбольшихсложных
задач состоитвтом, чтобыразбитьбольшуюзадачунанаборменьших, которыемож-
но решитьболееилименеенезависимоизатемскомбинироватьполученныерешения
для полученияполногорешения. Натакомподходеоснованаметодологияструктур-
ного программирования, котороегосподствоваловразработкепрограммногообеспе-
чения допоявления объектно-ориентированногоподхода.
При структурномпрограммированиибольшаяпрограммаразделяетсянанабор
более илименеенезависимых
. ВСи++ подпрограммыназываются
(вПаскалеинекоторыхдругих языкахпрограммированияестьдватипа
подпрограмм – «процедуры» и «функции»).
Подпрограммы уженеоднократновстречалисьвпредыдущихлекциях. Напри-
мер, впрограмме 2.3.2 дляпостроениятаблицыквадратныхкорнейбылприменен
следующий циклfor:
…
#include<math.h>
…
…
for ( number=1 ; number<=10 ; number=number+1 )
{
cout.width(20);
cout << number << sqrt(number) << «n»;
}
…
Функция «sqrt(…)» –этоподпрограмма, описаниекоторойхранитсявзаголо-
вочном файле «math.h», ареализация–вбиблиотечномфайле «math.lib». Привызове
функции «sqrt(…)» ейпередаетсячисловойпараметр «number», функцияприменяет
алгоритм вычисленияквадратногокорняизэтогочисла, изатемвозвращаетвычис-
ленное значениеобратновместовызова. Дляпримененияэтойфункциипрограмми-
сту совсемнеобязательнознать, какойименноалгоритмреализованвнутринее. Глав-
ное, чтобыфункциягарантированновозвращалаверныйрезультат. Былобыдовольно
нелепо включатьвявномвидеалгоритмизвлеченияквадратногокорня (и, возможно,
делать этонеоднократно) вглавнуюфункциюпрограммы «main».
В даннойлекцииописывается, какпрограммистможетопределятьсвоисобст-
венные функции. Сначалапредполагается, чтоэтифункцииразмещаютсяводном
файле сфункцией «main». Вконцелекциипоказывается, какраспределятьфункции
программы понесколькимфайлам.
Простым примеромопределенияииспользованияновойфункцииявляется
программа 2.1 (внейпользовательскаяфункцияназывается «area(…)»). Этапро-
грамма вычисляетплощадьпрямоугольниказаданнойдлиныиширины.
#include<iostream.h>
int area(int length, int width); /* Описание функции */
32
// ГЛАВНАЯ ФУНКЦИЯ:
int main()
{
int this_length, this_width;
cout << «Введите длину: «; /* <— строка 10 */
cin >> this_length;
cout << «Введите ширину: «;
cin >> this_width;
cout << «n»; /* <— строка 14 */
cout << «Площадь прямоугольника с размерами «;
cout << this_length << «x» << this_width;
cout << » равна » << area(this_length, this_width) << «n»;
return 0;
}
// КОНЕЦ ГЛАВНОЙ ФУНКЦИИ
// ФУНКЦИЯ ВЫЧИСЛЕНИЯ площади:
int area(int length, int width) /* Начало определения функции */
{
int number;
number = length * width;
return number;
} /* Конец определения функции */
// КОНЕЦ ФУНКЦИИ
Программа 2.1.
Программа 2.1, конечно, допускаетзаписьвболеесжатойформе, новданном
виде онаслужитхорошейиллюстрациейнекоторыхсвойствфункцийСи++:
•Структура определения (реализации) функцииподобнаструктурефункции
«main()» –втелефункцииестьописаниялокальныхпеременныхииспол-
няемые операторы.
•У функциимогутбытьпараметры, которыеуказываютсявспискевнутри
круглых скобокпослеименифункции. Укаждогопараметразадаетсятип.
•Если вызовфункциивстречаетсяранееееопределения, товначалепро-
граммы должносодержатьсяописаниефункции (прототип). Прототип
функции описываетеепараметрыитипвозвращаемогозначения. Обычно
прототипы функцийразмещаютсяпослеописанияглобальныхконстант.
Внутри функцииможетбытьнесколькооператороввозврата «return». Функ-
ция завершаетсяпослевыполнениялюбогооператора «return». например:
double absolute_value(double number)
{
if (number >= 0)
return number;
else
return -number;
}
33
Во всехрассмотренныхдосихпорпримерахпараметры функцийпередавались
. Привызовеизфункции «main()» вызываемойфункциипередаются
указанных переменных. например, впрограмме 2.1 функции «area(…)» переда-
ются текущиезначенияпеременных «this_length» и «this_width». Затемфункция
«area(…)» сохраняетпереданныезначениявсобственныхлокальныхпеременных, и
именно этипеременныеучаствуютвпоследующихвычисленияхвнутрифункции.
3.1 Передача параметров по значению
Функции, принимающиепараметры
, «безопасны» втомсмысле,
что онинемогутслучайноизменитьпеременныевызывающейфункции (т.е. уфунк-
ций нетскрытых
). Большинствофункцийпроектируютсяименно
таким образом.
программа 3.1 поясняет, почемуважногарантировать «сохранность» перемен-
ных вызывающейфункции. Этапрограммадолжнавыводитьнаэкранфакториали
корень изчисла, введенногопользователем:
Введите положительное число:
4
Факториал 4! равен 24, а квадратный корень из 4 равен 2.
Для извлеченияквадратногокорняприменяетсябиблиотечнаяфункция
«sqrt(…)». Библиотечнойфункциидлявычисленияфакториаланет, поэтомупри-
дется написатьсобственнуюфункцию (вычисляющуюдлялюбогоположительного
целого числаn
#include<iostream.h>
#include<math.h>
int factorial(int number);
// ГЛАВНАЯ ФУНКЦИЯ:
int main()
{
int whole_number;
cout << «Введите положительное число:n»;
cin >> whole_number;
cout << «Факториал » << whole_number << «! равен «;
cout << factorial(whole_number);
cout << «, а квадратный корень из » << whole_number;
cout << » равен » << sqrt(whole_number) << «.n»;
return 0;
}
// КОНЕЦ ГЛАВНОЙ ФУНКЦИИ
// ФУНКЦИЯ ДЛЯ ВЫЧИСЛЕНИЯ ФАКТОРИАЛА:
int factorial(int number)
{
int product = 1;
for ( ; number > 0 ; number—)
product *= number;
34
return product;
}
// КОНЕЦ ФУНКЦИИ
программа 3.1.
Если быфункция «factorial(…)» изменялапеременнуювызывающей
функции, топрограмма 3.1 выдавалабыследующийответ (формальноправильный,
но посмыслузадачинекорректный):
Введите положительное число:
4
Факториал 4! равен 24, а квадратный корень из 0 равен 0.
3.2 Передача параметров по ссылке
Все-такииногдабываетнеобходимо, чтобыфункцияизменилазначениепере-
данного ейпараметра. Рассмотримпрограмму 2.1. С 10-йпо 14-юстрокувнейвы-
полняется запросразмеров прямоугольника, азатемвычисляетсяегоплощадь.
При структурномпрограммированиинезависимыепосмыслучастипрограммы
принято оформлятьввидеотдельныхфункций. Дляполученияданныхотпользова-
теля создадимфункцию «get_dimensions». Вданномслучаенеобходимо, чтобыэта
функция изменялазначенияпеременных «this_length» и «this_width» (переданныхей
в качествепараметров) –помещалавнихзначения, введенныепользователемскла-
виатуры. Изменениепараметровфункциивозможноприпередачепараметров
. Утакихпараметроввзаголовкефункциипослеименитипауказываетсясим-
вол «&».
#include<iostream.h>
int area( int length, int width );
void get_dimensions( int& length, int& width );
// ГЛАВНАЯ ФУНКЦИЯ:
int main()
{
int this_length, this_width;
get_dimensions( this_length, this_width );
cout << «Площадь прямоугольника с размерами «;
cout << this_length << «x» << this_width;
cout << » равна » << area( this_length, this_width ) << «n»;
return 0;
}
// КОНЕЦ ГЛАВНОЙ ФУНКЦИИ
// ФУНКЦИЯ ВВОДА РАЗМЕРОВ ПРЯМОУГОЛЬНИКА:
void get_dimensions( int& length, int& width )
{
cout << «Введите длину: «;
cin >> length;
cout << «Введите ширину: «;
cin >> width;
cout << «n»;
35
}
// КОНЕЦ ФУНКЦИИ
// ФУНКЦИЯ ВЫЧИСЛЕНИЯ площади:
int area( int length, int width )
{
return length*width;
}
// КОНЕЦ ФУНКЦИИ
программа 3.2.
Функция _______»get_dimensions» изменяетзначенияпараметров «this_length» и
«this_width», ноневозвращаетникакогозначения (т.е. неявляется «функцией» вма-
тематическом смысле). Этотфактотражаетсяивпрототипе, ивопределениифунк-
ции –вкачествевозвращаемогозначенияуказантип «void» («пустой» тип).
Одним изхарактерныхсвойствобъектно-ориентированногоязыка, втомчисле
и Си++, является
–использованиеодногоименидлявыполненияраз-
личных действийнадразличнымиобъектами. Применительнокфункциямэтоназы-
вается
. ДляосновныхоперацийСи++ перегрузкаужевстроенавязык:
например, усложениясуществуеттолькоодноимя, «+», ноегоможноприменятьдля
сложения какцелых, такивещественныхзначений. Этаидеярасширяетсянаобра-
ботку операций, определенныхпользователем, т.е., функций.
Перегруженные функцииимеютодинаковыеимена, норазныеспискипарамет-
ров ивозвращаемыезначения (см. программу 4.1).
#include<iostream.h>
int average( int first_number, int second_number,
int third_number );
int average( int first_number, int second_number );
// ГЛАВНАЯ ФУНКЦИЯ:
int main()
{
int number_A = 5, number_B = 3, number_C = 10;
cout << «Целочисленное среднее чисел » << number_A << » и «;
cout << number_B << » равно «;
cout << average(number_A, number_B) << «.nn»;
cout << «Целочисленное среднее чисел » << number_A << «, «;
cout << number_B << » и » << number_C << » равно «;
cout << average(number_A, number_B, number_C) << «.n»;
return 0;
}
// КОНЕЦ ГЛАВНОЙ ФУНКЦИИ
// ФУНКЦИЯ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 3-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number,
int third_number )
36
{
return ((first_number + second_number + third_number)/3);
}
// КОНЕЦ ФУНКЦИИ
// ФУНКЦИЯ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 2-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number )
{
return ((first_number + second_number)/2);
}
// КОНЕЦ ФУНКЦИИ
программа 4.1.
Программа 4.1. выводитнаэкрансообщения:
Целочисленное среднее чисел 5 и 3 равно 4.
Целочисленное среднее чисел 5, 3 и 10 равно 6.
Функции помогаютприменятьдляразработкипрограммструктурныйметод
проектирования «
«. Приэтомрешаемаязадачаделитсянаподзадачи (иза-
тем напод-подзадачиит.д.). Длярешениякаждойподзадачипрограммистреализует
отдельную функцию, приэтомемуненужнознатьдеталиреализацииостальных
функций.
Чтобы функциеймогвоспользоватьсядругойпрограммист, онадолжнаиметь
осмысленное имяикомментарийсописаниемназначенияфункции, еепараметров и
возможных возвращаемыхзначений.
Опытные программистынаначальныхэтапахразработкичастоприменяют
пустые функции (
), которыесодержаттолькооператорвозвратазначениясо-
ответствующего типа. Этифункцийоблегчаютотладкуглавнойфункцииилипросто
функции болеевысокогоуровня.
Выделение врешаемойзадачефункций методом «сверхувниз» частоназывает-
ся
или
. Припроектированиинезависимых
друг отдругафункцийширокоприменяетсяпередачапараметровпозначениюило-
кальные переменные внутрифункций. Послереализациипрограммистможетрас-
сматривать подобныефункциикак «черныеящики». Дляихиспользованиязнатьде-
тали реализациинеобязательно.
Помимо метода «сверхувниз», вторымважнымметодомструктурногопроек-
тирования являетсяметодмодульногопрограммирования. Онпредполагаетразделе-
ние текстапрограммынанесколькофайлов, вкаждомизкоторыхсосредоточеныне-
зависимые частипрограммы (сгруппированныепосмыслуфункции).
В программахнаСи++ частоприменяютсябиблиотечныефункции (например,
«sqrt(…)»). Дляиспользованиябольшинства функций, втомчислеибиблиотечных,
необходимы двафайла (вскобкахпримерыданыдля «sqrt(…)»):
•
(«math.h») спрототипомфункции («sqrt(…)» имногих
других математическихфункций). Поэтомувпрограммах, вызывающих
37
«sqrt(…)», естьстрока «#include <math.h>», анеявноеобъявлениеэтой
функции.
•
(дляпользовательскихфункцийэтофайлысисходным
текстом наСи++, абиблиотечныефункцииобычнохранятсявскомпилиро-
ванном видевспециальныхбиблиотечных файлах, например,
«libcmtd.lib»). Файлыреализациипользовательскихфункций (обычнос
расширением «.cpp») содержатопределенияэтихфункций.
Разделение исходноготекстаназаголовочныефайлыифайлыреализациипо-
казано впрограмме 6.1, котораявыполняеттежедействия, чтоипрограмма 4.1. Те-
перь программасостоитизтрехфайлов: главногофайла, заголовочногофайласопи-
саниями двухфункцийрасчетасреднегозначения, исоответствующегофайлареали-
зации.
В главномфайлесодержитсяследующийтекст:
#include <iostream.h>
#include «averages.h»
int main()
{
int number_A = 5, number_B = 3, number_C = 10;
cout << «Целочисленное среднее чисел » << number_A << » и «;
cout << number_B << » равно «;
cout << average(number_A, number_B) << «.nn»;
cout << «Целочисленное среднее чисел » << number_A << «, «;
cout << number_B << » и » << number_C << » равно «;
cout << average(number_A, number_B, number_C) << «.n»;
return 0;
}
Главный файл программы 6.1.
Обратите внимание, чтоимяфайластандартнойбиблиотеки «iostream.h» вди-
рективе препроцессора «include» заключеновугловыескобки («<>»). Файлысимена-
ми вугловыхскобкахпрепроцессорищетвбиблиотечныхкаталогах, указанныхвна-
стройках компилятора. Именапользовательскихзаголовочныхфайловобычноза-
ключаются вдвойныекавычки, ипрепроцессорищетихвтекущемкаталогепро-
граммы.
Далее приведеносодержимоефайла «averages.h». Внем естьидентификатор
препроцессора «AVERAGES_H» ислужебныесловапрепроцессора «ifndef» («еслинеоп-
ределено»), «define» («определить») и «endif» («конецдирективыif»). Идентифика-
тор «AVERAGES_H» являетсяглобальным символическимименемзаголовочногофайла.
Первые двестрокифайласлужатзащитойотповторнойобработкитекстазаголовоч-
ного файлапрепроцессором, наслучай, есливисходномтекстепрограммыстрока
«#include «averages.h»» встречаетсянесколькораз.
В заголовочныхфайлах, кромепрототиповфункций, часторазмещаютсяопи-
сания глобальных константипользовательскихтипов. Подробнееобэтомговоритсяв
курсе объектно-ориентированногопрограммирования.
#ifndef AVERAGES_H
# define AVERAGES_H
38
// (определения констант и пользовательских типов)
// ПРОТОТИП ФУНКЦИИ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 3-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number,
int third_number );
// ПРОТОТИП ФУНКЦИИ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 2-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number );
#endif
Заголовочный файл averages.h.
Ниже показаносодержимоефайла «averages.cpp» сисходнымтекстомполь-
зовательских функций:
#include <iostream.h>
#include «averages.h»
// ФУНКЦИЯ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 3-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number,
int third_number )
{
return ((first_number + second_number + third_number)/3);
}
// ФУНКЦИЯ ДЛЯ ВЫЧИСЛЕНИЯ ЦЕЛОЧИСЛЕННОГО СРЕДНЕГО
// значения 2-Х ЦЕЛЫХ ЧИСЕЛ:
int average( int first_number, int second_number )
{
return ((first_number + second_number)/2);
}
файл реализации averages.cpp.
Программа
6.1
демонстрирует основное достоинство модульного подхода
:
при
изменении деталей реализации в файле
«
averages.cpp
»
не обязательно вносить из
—
менения в файл
«
averages.h
»
или в главный файл программы
.
В данной лекции описано
,
как в Си
++
можно создавать новые функции
.
Есть
два способа передачи параметров внутрь функции
:
по значению и по ссылке
.
Функ
—
ции облегчают применение процедурной абстракции при разработке программ мето
—
дом
«
сверху вниз
«.
При модульном подходе описание и реализация функций разме
—
щаются в отдельных файлах
,
в таком случае для вызова функции необходимо вклю
—
чать в текст программы заголовочный файл
.
39
Упражнение 1
В программе из упражнения
2
лекции
2 (
файл
ex2_2.cpp)
выделите
6
функций
,
имена и назначение которых перечислены ниже
:
fahrenheit_of
Возвращает
ного значения по шкале Цельсия.
absolute_value_of
Возвращает
ного значения по шкале Цельсия.
print_preliminary_Message
Печать сообщения
,
поясняющего назначение программы
.
input_table_specifications
запрос параметров таблицы с клавиатуры
.
print_message_echoing_input
Повторное отображение параметров
,
введенных пользователем
.
print_table
Печать таблицы температур
.
Проверьте свою программу для различных исходных данных
.
В качестве кон
—
трольного примера можете использовать следующие выходные данные
:
Эта программа печатает значения температур в разных шкалах.
Введите минимальное (целое)
по Цельсию, которое будет в первой строке таблицы:
0
Введите максимальное
100
Введите разницу температур между соседними строками таблицы:
20
Преобразование значений температуры от 0 градусов Цельсия
до 100 градусов Цельсия, с шагом 20 градусов:
Цельсий Фаренгейт Абсолютная температура
0 32.00 273.15
20 68.00 293.15
40 104.00 313.15
… … …
… … …
100 212.00 485.15
Упражнение 2
Разделите программу из упражнения
1
на три файла
:
1)
главный файл программы
;
2)
заголовочный файл
«
conversions.h
»
с прототипами функций
«
fahrenheit_of(…)
»
и
«
absolute_value_of(…)
«;
3)
файл реализации с определением этих двух функций
.
Снова проверьте свою программу для различных исходных данных
.
40
Упражнение 3
(
а
)
Создайте заголовочный файл
«
statistics.h
»
и соответствующий файл реализа
—
ции
«
statistics.cpp
»
с функциями
«
average(…)
»
и
«
standard_deviation(…)
«.
Эти функции должны вычислять среднее
—
нение для последовательности из
1, 2, 3
или
4
вещественных чисел
.
Среднеквадра
—
тическое отклонение чисел
1
, …,
N
определяется как корень из среднего значения
квадратов отклонений чисел от своего среднего
:
Σ
=
= −
1
2
) ( 1
σ
,
где
Σ
=
=
1
1
(
б
)
Напишите тестовую программу для проверки функций из файла
«
statistics.h
«,
которая в цикле запрашивает исходные данные до тех пор
,
пока пользователь не
сообщит о завершении работы
(
некоторым специально оговоренным числом
).
Ва
—
ша тестовая программа должна выдавать на экран сообщения
,
подобные приве
—
денным ниже
:
Эта программа предназначена для тестирования функций из
заголовочного файла «statistics.h».
сколько чисел будет в тестовой последовательности – 1, 2, 3
или 4? (для завершения работы введите 0):
3
Введите первое число:
5
Введите второе число:
7
Введите третье число:
9
Среднее
Сколько чисел будет в тестовой последовательности – 1, 2, 3
или 4? (для завершения работы введите 0):
1
Введите первое число:
5.8
Среднее
Сколько чисел будет в тестовой последовательности – 1, 2, 3
или 4? (для завершения работы введите 0):
8
Извините, но эта программа может работать только с 1, 2, 3
или 4-мя числами.
Сколько чисел будет в тестовой последовательности – 1, 2, 3
или 4? (для завершения работы введите 0):
0
Программа тестирования функций из заголовочного файла
«statistics.h» завершила работу.
test_three_values()
main()
for
while
41
ЛЕКЦИЯ 4. Текстовые файлы и потоки ввода/вывода
Во всех рассматривавшихся до сих пор программах ввод данных производился
только с клавиатуры
,
а вывод
–
только на экран
.
Если в качестве устройств вво
—
да
/
вывода ограничиться только клавиатурой и экраном
,
то в таком случае будет
сложно обработать большие объемы входных данных
.
Выходные данные
,
отображен
—
ные на экране
,
после выключения компьютера безвозвратно теряются
.
Для устранения подобных проблем удобно сохранять данные на запоминаю
—
щих устройствах
,
предназначенных для долговременного хранения данных
(
обычно
это магнитные диски
).
Данные
,
сгенерированные с помощью одной программы
,
мож
—
но сохранить на диске и в дальнейшем
,
при необходимости
,
извлечь и обработать в
другой программе
.
На дисках данные хранятся в виде структур данных
,
обслуживаемых операци
—
онной системой
,
–
в виде
.
файл проще всего представить как линейную по
—
следовательность символов
.
текст этой лекции
(
если не учитывать специальные сим
—
волы форматирования
)
можно сохранить в файле с именем
«Lecture_4.txt»
в виде
(
рис
. 1):
Рис. 1. Начало файла
«
lecture_4.txt
«.
Перед началом изучения файловых операций в Си
++,
необходимо ознакомить
—
ся с понятием
.
Поток напоминает
«
канал
»
или
«
трубу
«,
через ко
—
торую данные поступают от передатчика к приемнику
.
Исключительно важная осо
—
бенность потоковой обработки данных состоит в том
,
что элементы данных можно
посылать или считывать из потока только по одному за раз
,
т
.
е
.
.
В данном курсе рассматриваются только однонаправленные потоки
,
в которых
данные всегда передаются в одном направлении
.
Из программы данные можно отпра
—
вить
(
записать
)
в
,
а получить
(
прочитать
)
их в программе из
.
например
,
сразу после запуска программы
,
поток стандартного ввода
«cin»
подключается к клавиатуре
,
а поток стандартного вывода
«cout»
–
к экрану
.
Потоки ввода
/
вывода
,
вроде
«cin»
и
«cout»,
являются примерами
класса
«
поток
«.
Поэтому изучение потоков полезно и по той причине
,
что позволяет
ознакомиться с элементами синтаксиса и некоторыми объектно
—
ориентированными
понятиями Си
++.
Список функций для работы с файловыми потоками хранится в заголовочном
файле
«fstream.h».
Поэтому во всех рассматриваемых ниже фрагментах программ
предполагается
,
что в начале программы есть соответствующая директива
«#include»:
#include<fstream.h>
2.1 Создание потоков
В программе перед первым обращением к потоку ввода или вывода необходи
—
мо
«
создать
»
поток
.
Операторы для создания потоков похожи на описания перемен
—
42
ных
,
и они обычно размещаются в начале программы или функции с описа
—
ниями переменных
.
Например
,
операторы
ifstream in_stream;
ofstream out_stream;
создают поток с именем
«in_stream»,
являющийся объектом
(
как типа данных
)
«ifstream» (input-file-stream,
файловый поток ввода
),
и поток с именем
«out_stream»,
являющийся объектом класса
«ofstream» (output-file-stream,
файловый поток вывода
).
Аналогию между потоками и обычными переменными
(
типа
«int», «char»
и т
.
д
.)
не
следует понимать слишком буквально
.
Например
,
к потокам нельзя применять Опера
—
тор присваивания
(
например
,
нельзя записать
«in_stream1 = in_stream2»).
2.2 Подключение и отключение потоков от файлов
После создания потока его можно подключить к файлу
(
открыть файл
)
с помо
—
щью
«open(…)». (
В предыдущих лекциях уже использовались несколь
—
ко функций
—
членов потоков вывода
.
например
,
во
2-
й лекции применялись
«precision(…)»
и
«width(…)».)
Функция
«open(…)»
у потоков
ifstream
и
ofstream
работает
по
—
разному
(
т
.
е
.
это полиморфная функция
).
Для подключения потока
ifstream
с именем
«in_stream»
к файлу с именем
«Lecture_4.txt»
надо применить следующий вызов
:
in_stream.open(«Lecture_4.txt»);
Этот оператор подключит поток
«in_stream»
к началу файла
«Lecture_4.txt»
(
графически состояние программы после выполнения оператора показано на рис
. 2).
Рис. 2. состояние программы после подключения потока ввода к файлу
.
Чтобы к файлу
«Lecture_4.txt»
подключить поток вывода
ofstream
с именем
«out_stream»,
надо выполнить аналогичный оператор
:
out_stream.open(«Lecture_4.txt»);
Этот оператор подключит поток
«out_stream»
к файлу
«Lecture_4.txt»,
но при
этом прежнее содержимое файла будет удалено
.
Файл будет подготовлен к приему
новых данных
(
рис
. 3).
Рис. 3. состояние программы после подключения потока вывода к файлу
.
Для отключения потока
«in_stream»
от файла
,
к которому он подключен
(
для
закрытия файла
),
надо вызвать функцию
—
член
«close()»:
43
in_stream.close();
После этого состояние программы по отношению к файлу будет таким
,
как на
рис
. 4.
Рис. 4. состояние программы после отключения потока ввода от файла
.
Функция
—
член отключения от файла у потока вывода
:
out_stream.close();
выполняет аналогичные действия
,
но
,
дополнительно
,
в конец файла добавляется
служебный символ
«end-of-file (
маркер конца файла
)».
Т
.
о
.,
даже если в поток вывода
не записывались никакие данных
,
то после отключения потока
«out_stream»
в файле
«Lecture_4.txt»
будет один служебный символ
(
рис
. 5).
В таком случае файл
«Lecture_4.txt»
останется на диске
,
но он будет
.
Рис. 5. состояние программы после отключения потока вывода от файла
,
в
который не было записано ни одного символа
..
Файловые операции
,
например
,
открытие и закрытие файлов
,
известны как
один из наиболее вероятных источников ошибок
.
В надежных коммерческих про
—
граммах всегда выполняется проверка
,
успешно или нет завершилась файловая опе
—
рация
.
В случае ошибки вызывается специальная функция
—
обработчик ошибки
.
Простейший способ проверки ошибок файловых операций заключается в вызо
—
ве функции
—
члена
«fail()».
Вызов
in_stream.fail();
возвращает истинное
(True),
если последняя операция потока
«in_stream»
привела к ошибке
(
может быть
,
была попытка открытия несуществующего файла
).
После ошибки поток
«in_stream»
может быть поврежден
,
поэтому лучше не продол
—
жать работу с ним
.
В приведенном ниже фрагменте программы в случае ошибки при открытии
файла на экран выдается сообщение и программа завершает работу с помощью биб
—
лиотечной функции
«exit()» (
она описана в файле
«stdlib.h»):
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
int main()
{
44
ifstream in_stream;
in_stream.open( «Lecture_4.txt» );
if ( in_stream.fail() )
{
cout << «Извините, открыть файл не удалось!n»;
exit(1);
}
…
4.1 Функция ввода «
get(…)
«
После того
,
как файл для ввода данных открыт
,
из него можно считывать от
—
дельные символы
.
Для этого служит функция
«get(…)».
У нее есть параметр типа
«char&».
Если программа находится в состоянии
,
как на рис
. 2,
то после вызова
:
in_stream.get(ch);
произойдет следующее
: (
а
)
переменной
«ch»
будет присвоено
«‘
Л
‘»,
и
(
б
)
по
—
ток
«in_stream»
будет подготовлен для чтения следующего символа
(
рис
. 6).
Рис. 6. состояние программы после чтения из файла первого символа
.
4.2 Функция вывода «
put(…)
«
С помощью потока вывода класса
ofstream
в открытый файл можно
отдельные символы
.
Для этого у класса
ofstream
есть функция
—
член
«put(…)».
Записываемый символ передается ей как параметр типа
«char».
Если программа пре
—
бывает в состоянии
,
представленном на рис
. 3,
то оператор
out_stream.put(‘Л’);
изменит состояние на то
,
которое показано на рис
. 7:
Рис. 7. состояние программы после записи в файл первого символа
.
4.3 Функция «
putback(…)
«
В Си
++
у потока
ifstream
есть функция
—
член
«
putback(…)
«.
На самом деле
она не
«
возвращает символ назад
» (
т
.
е
.
не изменяет содержимого файла ввода
),
но ве
—
дет себя так
,
как будто это делает
.
На рис
. 8
показано состояние
,
в которое перейдет
программа из состояния рис
. 6
после выполнения оператора
:
45
in_stream.putback(ch);
Рис. 8. состояние программы после вызова функции
«
putback(‘Л’)
«.
«
Вернуть назад
»
можно любой символ
.
состояние программы после вызова
in_stream.putback(‘7’);
показано на рис
. 9.
Рис. 9. состояние программы после вызова функции
«
putback(‘7’)
«.
5.1 Проверка конца файла с помощью функции «
eof()
«
При работе с потоком ввода надо следить за тем
,
чтобы не пропустить момент
достижения конца файла
.
В большинстве реализаций Си
++ (
в том числе и в Microsoft
Visual C++
)
в класс
«
поток ввода
»
встроен
«
конец файла
(end-of-file, EOF)»
и
функция
—
член
«
eof()
»
для чтения этого флага
.
С помощью функции
«
eof()
»
можно
узнать
,
находится ли в данный момент флаг в состоянии
True
(
конец файла достиг
—
нут
)
или
False
(
конца файла пока нет
).
При открытии файла
,
когда поток
ifstream
только подключается к нему
,
флаг
EOF
сбрасывается в
False
(
даже если файл пуст
).
Но
,
если
ifstream
«
in_stream
»
сейчас расположен в конце файла
,
и флаг
EOF
равен
False
,
то после вы
—
зова
in_stream.get(ch);
переменная
«
ch
»
окажется в неопределенном состоянии
,
а флагу
EOF
будет присвоено
True
.
Если флаг
EOF
равен
True
,
то программа не должна пытаться выпол
—
нить чтение из файла
,
поскольку результат чтения будет неопределенным
.
Допустим
,
программа находится с состоянии
,
показанном на рис
. 10.
46
Рис. 10. состояние программы после чтения предпоследнего символа из файла
.
Тогда после выполнения оператора
in_stream.get(ch);
программа перейдет в состояние
,
изображенное на рис
. 11.
Рис. 11. состояние программы после чтения последнего символа из файла
.
Теперь логическое выражение
in_stream.eof()
будет иметь истинное
True
.
Если снова выполнить чтение символа
:
in_stream.get(ch);
то в результате получится состояние
,
показанное на рис
. 12.
Рис. 12. состояние программы после операции чтения символа при установ
—
ленном флаге
EOF.
Ниже приведена простая программа для копирования текстового файла
«
Lecture_4
.txt
»
одновременно и на экран
,
и в другой файл
«
Copy_of_4.txt
«.
Для
прекращения копирования применяется вызов функции
«
eof()
«.
Обратите внимание
на цикл
«
while
«.
Цикл с префиксным условием
(
предусловием
) «
while
»
является уп
—
рощенным вариантом цикла
«
for
«.
У цикла
«
while
»
в круглых скобках
«
()
»
нет опе
—
раторов инициализации и изменения значений
(
подробно эти циклы рассматриваются
в следующей лекции
).
#include <iostream.h>
#include <fstream.h>
int main()
{
char character;
ifstream in_stream;
ofstream out_stream;
47
in_stream.open( «Lecture_4.txt» );
out_stream.open( «Copy_of_4.txt» );
in_stream.get( character );
while ( !in_stream.eof() )
{
cout << character;
out_stream.put(character);
in_stream.get(character);
}
out_stream.close();
in_stream.close();
return 0;
}
программа 5.1.
Потоки можно использовать в качестве параметров функций
,
но их
надо передавать
(
а не по значению
).
Ниже приведен усовершенствован
—
ный вариант программы
5.1,
в котором копирование выполняется специальной функ
—
цией
«
copy_to(…)
«.
#include <iostream.h>
#include <fstream.h>
void copy_to( ifstream& in, ofstream& out );
// Главная функция
int main()
{
ifstream in_stream;
ofstream out_stream;
in_stream.open( «Lecture_4.txt» );
out_stream.open( «Copy_of_4.txt» );
copy_to( in_stream, out_stream );
out_stream.close();
in_stream.close();
return 0;
}
// Конец главной функции
// Функция для копирования файла в другой файл и на экран
void copy_to( ifstream& in, ofstream& out )
{
char character;
in.get( character );
while ( !in.eof() )
{
cout << character;
out.put( character );
48
in.get( character );
}
}
// Конец функции
программа 6.1.
До сих пор рассматривались способы записи и чтения из файлов отдельных
символов
.
На нижнем уровне
,
скрытом внутри классов
ofstream
and
ifstream
,
объ
—
екты этих классов всегда работают с файлами как с последовательностями символов
.
Поэтому данные других типов
(«
int
«, «
double
»
и др
.)
для записи в файл должны быть
преобразованы в последовательность символов
.
При чтении из файла эти последова
—
тельности должны быть преобразованы обратно
.
Некоторые преобразования типов данных автоматически выполняются Опера
—
торами
«
>>
»
и
«
<<
» (
в предыдущих лекциях они часто использовались для ввода с кла
—
виатуры и вывода на экран
).
например
,
из состояния
,
показанного на рис
. 13:
Рис. 13. состояние программы после открытия файла вывода
(
после подклю
—
чения потока вывода к файлу
).
с помощью оператора
out_stream << 437 << ‘ ‘;
программа перейдет в новое состояние
,
изображенное на рис
. 14.
Рис. 14. состояние программы после записи в поток вывода целого значения
«
437
»
и пробела
.
При использовании операторов
«
>>
»
и
«
<<
»
обязательно надо после каждого за
—
писанного значения записывать еще как минимум один символ
‘ ‘
(
пробел
)
или слу
—
жебный символ
‘n’
(
маркер конца строки
).
Это гарантирует
,
что элементы данных
будут корректно отделены в файле друг от друга
,
и их можно будет извлекать оттуда
с помощью оператора
«
>>
«.
например
,
в состоянии
,
показанном на рис
. 15:
49
Рис. 15. состояние программы в некоторый момент времени
,
когда текущая
позиция потока ввода установлена на пробел
.
если
«
n
»
является переменной типа
«
int
»
и имеет
10
,
то выполнение опера
—
тора
in_stream >> n;
приведет к состоянию
,
показанному на рис
. 16.
Рис. 16. состояние программы после чтения из потока ввода целого значения
«
437
«.
Обратите внимание
,
что оператор
«
>>
»
перед числом
437
пропустил пробел
‘ ‘
.
Этот оператор всегда пропускает пробелы
,
независимо от типа считываемых данных
(
даже при чтении символов
).
Работа с операторами
«
>>
»
и
«
<<
»
продемонстрирована в программе
7.1.
Снача
—
ла она создает файл
«
Integers.txt
«,
записывает в него целые числа
51
,
52
,
53
,
54
и
55
,
а затем считывает эти числа из файла
.
#include <iostream.h>
#include <fstream.h>
int main()
{
char character;
int number = 51;
int count = 0;
ofstream out_stream;
ifstream in_stream1; // Поток для подсчета целых чисел.
ifstream in_stream2; // Поток для подсчета символов.
// Создание файла
out_stream.open( «Integers.txt» );
for ( count = 1; count <= 5; count++ )
out_stream << number++ << ‘ ‘;
out_stream.close();
// Подсчет количества целых чисел в файле
in_stream1.open( «Integers.txt» );
count = 0;
in_stream1 >> number;
while ( !in_stream1.eof() )
{
50
count++;
in_stream1 >> number;
}
in_stream1.close();
cout << «В файле хранится » << count << » целых чисел,n»;
// Подсчет количества символов, не являющихся разделителями
in_stream2.open( «Integers.txt» );
count = 0;
in_stream2 >> character;
while ( !in_stream2.eof() )
{
count++;
in_stream2 >> character;
}
in_stream2.close();
cout << «представленных с помощью » << count << » символов.n»;
return 0;
}
программа 7.1.
Программа
7.1
выведет на экран следующие сообщения
:
В файле хранится 5 целых чисел,
представленных с помощью 10 символов.
При подсчете символов в последней части программы
7.1
снова обратите вни
—
мание на то
,
что
,
в отличие от функции
«
get(…)
«,
оператор
«
>>
»
игнорирует в файле
пробелы
(
которые разделяют пять целых чисел
).
В лекции рассмотрены способы работы с текстовыми файлами с помощью по
—
токов ввода
/
вывода
. «
Низкоуровневый
»
ввод
/
вывод выполняется с помощью функций
«
get(…)
»
и
«
put(…)
«,
а
«
высокоуровневый
»
ввод
/
вывод значений разных типов
–
с
помощью потоковых операторов
«
>>
»
и
«
<<
«.
Упражнение 1
Напишите программу
,
печатающую на экране содержимое собственного ис
—
ходного файла на Си
++.
Упражнение 2
Разработайте программу
,
которая
(1)
начинается с оператора вывода тестового
сообщения
:
cout << «Проверка: » << 16/2 << » = » << 4*2 << «.nn»;
и затем
(2)
копирует собственный исходный файл на Си
++
в файл
«
WithoutComments.cpp
»
и на экран
,
при этом пропуская все комментарии между
маркерами
«
/* … */
» (
и маркеры комментариев тоже
).
51
Получившийся файл
«
WithoutComments.cpp
»
должен компилироваться и ра
—
ботать точно так же
,
как и исходная программа
.
putback()
Упражнение 3
Напишите программу
,
которая подсчитывает и выводит на экран количество
символов
(
включая пробелы
)
в собственном исходном файле
.
Упражнение 4
Без использования массива
(
массивы будут рассмотрены в
6-
й лекции
)
напи
—
шите программу
,
которая печатает на экране собственный исходный файл в обратном
порядке
.
Упражнение 5
Что выведет на экран следующая программа
?
#include <iostream.h>
#include <fstream.h>
int main()
{
char character;
int integer;
ofstream out_stream;
ifstream in_stream;
// Создание текстового файла и запись в него двух целых чисел
out_stream.open( «Integers.txt» );
out_stream << 123 << ‘ ‘ << 456;
out_stream.close();
// Попытка чтения из только что созданного файла
// «Integers.txt» символа, затем целого числа,
// затем снова символа, затем опять целого числа.
in_stream.open( «Integers.txt» );
in_stream >> character >> integer;
cout << «символ: ‘» << character << «‘n»;
cout << «целое число: » << integer << «n»;
in_stream >> character >> integer;
c
out << «символ: ‘» << character << «‘n»;
cout << «целое число: » << integer << «n»;
in_stream.close();
return 0;
}
52
ЛЕКЦИЯ 5. Операторы ветвления и циклы
В этой лекции подробно рассматриваются операторы ветвления
(«
if
»
и
«
switch
«)
и операторы циклов
«
for
»
и
«
while
«.
Для применения всех этих операто
—
ров необходимо хорошо знать
,
что такое логические выражения и как они вычисля
—
ются
.
язык Си
++
унаследовал от языка Си соглашение
,
согласно которому целое
0
считается логическим
«
false
» (
ложное
),
а ненулевое целое
–
ло
—
гическим
«
true
» (
истинным значением
).
Но выражения вроде
условие1 == 1
или
условие2 == 0
не слишком удобны при чтении теста программ человеком
.
Было бы лучше записы
—
вать логические выражения в интуитивно понятном виде
:
условие1 == true
и
условие2 == false
Поэтому в Си
++
был добавлен специальный логический тип
«
bool
«.
Перемен
—
ные типа
«
bool
»
могут принимать значения
«
true
»
и
«
false
«,
которые при необхо
—
димости автоматически преобразуются в выражениях в значения
1
и
0
.
Тип данных
«
bool
»
можно использовать в программах точно так же
,
как и ти
—
пы
«
int
«, «
char
»
и др
. (
например
,
для описания переменных или для создания функ
—
ций
,
возвращающих значения типа
«
bool
«).
Программа
1.1
приведена в качестве примера использования типа данных
«
bool
«.
Она запрашивает с клавиатуры возраст кандидата
,
сдававшего некий тест
,
и
полученную кандидатом оценку в баллах
.
Затем программа оценивает результат вы
—
полнения теста по шкале
,
зависящей от возраста кандидата и делает вывод о том
,
сдан
тест или нет
.
Для кандидатов до
14
лет порог сдачи теста составляет
50
баллов
,
для
15
или
16
лет
–
55
баллов
,
старше
16-
ти лет
–
60
баллов
.
#include <iostream.h>
bool acceptable( int age, int score );
int main()
{
int candidate_age, candidate_score;
cout << «Введите возраст кандидата: «;
cin >> candidate_age;
cout << «Введите результат тестирования: «;
cin >> candidate_score;
if ( acceptable( candidate_age, candidate_score ) )
cout << «Этот кандидат сдал тест успешно.n»;
else
cout << «Этот кандидат тест не прошел.n»;
53
return 0;
}
// Функция оценки результата тестирования: тест сдан/не сдан
bool acceptable( int age, int score )
{
if ( age <= 14 && score >= 50 )
return true;
else if ( age <= 16 && score >= 55 )
return true;
else if ( score >= 60 )
return true;
else
return false;
}
программа 1.1.
Циклы
«
for
»
впервые встречались во
2-
й лекции
,
цикл
«
while
»
упоминался в
4-
й лекции
.
Любой цикл
«
for
»
можно переписать в виде цикла
«
while
»
и наоборот
.
Рассмотрим программу
2.1 (
она похожа на программу
2.2
из
2-
й лекции
).
#include <iostream.h>
int main()
{
int number;
char character;
for ( number = 32; number <= 126; number = number + 1 )
{
character = number;
cout << «Символ ‘» << character;
cout << «‘ имеет код » << number << «n»;
}
return 0;
}
программа 2.1.
Программу
2.1
можно переписать с помощью цикла
«
while
» (
программа
2.2):
#include <iostream.h>
int main()
{
int number;
char character;
number = 32;
while ( number <= 126 )
{
character = number;
cout << «Символ ‘» << character;
cout << «‘ имеет код » << number << «n»;
number++;
54
}
return 0;
}
программа 2.2.
Замена цикла
«
while
»
на цикл
«
for
»
выполняется совсем просто
.
Например
,
в
программе
2.2
строку
while (number <= 126)
можно заменить эквивалентным циклом
«
for
»
без операторов инициализации и из
—
менения значений
:
for ( ; number <= 126; )
В Си
++
есть еще один
,
третий
,
вариант цикла
–
оператор цикла с постфиксным
условием
(
постусловием
)
.
Он отличается от циклов
«
for
»
и
«
while
«
тем
,
что тело цикла внутри скобок
«
{}
»
обязательно выполняется как минимум один
раз
,
т
.
к
.
условие повторения цикла проверяется только после выполнения тела цикла
.
Циклы
«
Do … while
»
используются довольно редко
.
Например этот цикл можно
применить для проверки корректности введенных с клавиатуры данных
(
програм
—
ма
2.2
а
).
…
…
do {
cout << «Введите результат тестирования: «;
cin >> candidate_score;
if ( candidate_score > 100 || candidate_score < 0 )
cout << «Допускается оценка от 0 до 100.n»;
} while ( candidate_score > 100 || candidate_score < 0 );
…
…
Фрагмент программы 2.2a.
В программе
2.2a
цикл с постусловием позволяет избавиться от дублирования
операторов печати приглашения и ввода данных
,
которое возникает при использова
—
нии эквивалентного цикла
«
while
» (
программа
2.2b).
…
…
cout << «Введите результат тестирования: «;
cin >> candidate_score;
while ( candidate_score > 100 || candidate_score < 0 )
{
cout << «Допускается оценка от 0 до 100.n»;
cout << «Введите результат тестирования: «;
cin >> candidate_score;
}
…
…
Фрагмент программы 2.2b.
55
Вложенные операторы
«
if
«,
предназначенные для выполнения
«
множествен
—
ного ветвления
«,
уже встречались в
1-
й лекции
.
Упрощенная версия этого примера
приведена ниже
:
…
…
if ( total_test_score < 50 )
cout << «Вы не прошли тест. Выучите материал как следует.n»;
else if ( total_test_score < 60 )
cout << «Вы прошли тест со средним результатом.n»;
else if ( total_test_score < 80 )
cout << «Вы хорошо выполнили тест.n»;
else if ( total_test_score < 100 )
cout << «Вы показали отличный результат.n»;
else
{
cout << «Вы выполнили тест нечестно»;
сout << «(оценка должна быть меньше 100 баллов)!n»;
}
…
…
Вложенные операторы
«
if
»
выглядят слишком громоздко
,
поэтому в Си
++
реализован еще один способ множественного ветвления
–
оператор
«
switch
«.
Он по
—
зволяет выбрать для выполнения один из нескольких операторов
,
в зависимости от
текущего значения определенной переменной или выражения
.
В приведенном выше примере сообщение для печати выбирается в зависимо
—
сти от значения переменной
«
total_test_score
«.
Это может быть произвольное целое
число в диапазоне от
0
до
100.
Диапазон проверяемых значений можно сузить
,
если
учесть
,
что оценка теста проверяется с точностью до
10-
ти баллов
.
Введем дополни
—
тельную целочисленную переменную
«
score_out_of_ten
»
и присвоим ей
:
score_out_of_ten = total_test_score/10;
Теперь проверку в программе можно сформулировать так
: (1)
если переменная
«
score_out_of_ten
»
равна
0, 1, 2, 3
или
4,
то печатать сообщение
«
Вы не прошли
тест. Выучите материал как следует.
«, (2)
если
«
score_out_of_ten
»
равна
5,
то печатать сообщение
«
Вы прошли тест со средним результатом.
»
и
т
.
д
.
В целом оператор
«
switch
»
будет выглядеть так
:
…
…
score_out_of_ten = total_test_score / 10;
switch ( score_out_of_ten )
{
case 0 :
case 1 :
case 2 :
case 3 :
case 4 :
cout << «Вы не прошли тест. Выучите материал как следует.n»;
break;
case 5 :
cout << «Вы прошли тест со средним результатом.n»;
break;
case 6 :
case 7 :
56
cout << «Вы хорошо выполнили тест.n»;
break;
case 8 :
case 9 :
case 10 :
cout << «Вы показали отличный результат.n»;
break;
default :
cout << «Вы выполнили тест нечестноn»;
сout << «(оценка не должна быть больше 100 баллов)!n»;
}
…
…
Фрагмент программы 3.1.
Оператор
«
switch
»
имеет следующий синтаксис
:
switch (
)
{
case
1 :
1
break;
…
…
…
case
N :
N
break;
default :
}
Сделаем несколько важных замечаний относительно оператора
«
switch
«:
•
Внутри
«
switch
»
выполняются операторы
,
содержащиеся между меткой
,
совпадающей с текущим значением селектора
,
и первым встретившимся по
—
сле этой метки оператором
«
break
«.
•
Операторы
«
break
»
необязательны
,
но они улучшают читабельность про
—
грамм
.
С ними сразу видно
,
где заканчивается каждый вариант множествен
—
ного ветвления
.
Как только при выполнении операторов внутри
«
switch
«
встречается
«
break
«,
то сразу выполняется переход на первый оператор про
—
граммы
,
расположенный после оператора
«
switch
«.
Иначе продолжается
последовательное выполнение операторов внутри
«
switch
«.
•
Селектор
(
переменная или выражение
)
может быть целочисленного
(
напри
—
мер
, «
int
»
или
«
char
«)
или любого перечислимого типа
,
но не веществен
—
ного типа
.
•
Вариант
«
default
» («
по умолчанию
«)
необязателен
,
но для безопасности
лучше его предусмотреть
.
В Си
++
фигурные скобки
«
{}
»
позволяют оформить составной оператор
,
кото
—
рый содержит несколько операторов
,
но во всех конструкциях языка может подстав
—
57
ляться как один оператор
.
На описания переменных фигурные скобки также оказы
—
вают важное влияние
.
Составной оператор
,
внутри которого описана одна или несколько переменных
,
называется
.
Для переменных
,
объявленных внутри блока
,
этот блок является
.
Другими словами
,
переменные
«
создаются
»
каждый раз
,
когда
при выполнении программа входит внутрь блока
,
и
«
уничтожаются
»
после выхода из
блока
.
Если одно и то же имя используется для переменной внутри и снаружи блока
,
то это две разных
,
независимых переменных
.
При выполнении внутри блока про
—
грамма по умолчанию полагает
,
что имя относится к внутренней переменной
.
Обра
—
щение к внешней переменной происходит только в том случае
,
если переменная с та
—
ким именем не описана внутри блока
.
действие этого правила продемонстрировано в
программе
4.1.
#include <iostream.h>
int integer1 = 1;
int integer2 = 2;
int integer3 = 3;
int main()
{
int integer1 = -1;
int integer2 = -2;
{
int integer1 = 10;
cout << «integer1 == » << integer1 << «n»;
cout << «integer2 == » << integer2 << «n»;
cout << «integer3 == » << integer3 << «n»;
}
cout << «integer1 == » << integer1 << «n»;
cout << «integer2 == » << integer2 << «n»;
cout << «integer3 == » << integer3 << «n»;
return 0;
}
программа 4.1.
Программа
4.1
выводит на экран сообщения
:
integer1 == 10
integer2 == -2
integer3 == 3
integer1 == -1
integer2 == -2
integer3 == 3
Применение локальных переменных иногда объясняется экономией памяти
,
а
иногда необходимостью использования в различных частях программы разных пере
—
менных с одинаковыми именами
.
См
.
в качестве примера программу
4.2,
которая пе
—
чатает таблицу умножения для чисел от
1
до
10.
#include <iostream.h>
int main()
{
int number;
for ( number = 1; number <= 10; number++ )
58
{
int multiplier;
for ( multiplier = 1; multiplier <= 10; multiplier++ )
{
cout << number << » x » << multiplier << » = «;
cout << number * multiplier << «n»;
}
cout << «n»;
}
return 0;
}
программа 4.2.
Программу
4.2
можно переписать в более понятном виде с помощью функ
—
ции
(
см
.
программу
4.3).
#include <iostream.h>
void print_times_table( int value, int lower, int upper );
int main()
{
int number;
for ( number = 1; number <= 10; number++ )
{
print_times_table( number, 1, 10 );
cout << «n»;
}
return 0;
}
void print_times_table( int value, int lower, int upper )
{
int multiplier;
for ( multiplier = lower; multiplier <= upper; multiplier++ )
{
cout << value << » x » << multiplier << » = «;
cout << value * multiplier << «n»;
}
}
программа 4.3.
Далее
,
программу
4.3
можно усовершенствовать
,
исключив описания всех пе
—
ременных из
«
main()
»
и добавив две функции
(
см
.
программу
4.4).
#include <iostream.h>
void print_tables( int smallest, int largest );
void print_times_table( int value, int lower, int upper );
int main()
{
print_tables( 1, 10 );
return 0;
}
void print_tables( int smallest, int largest )
59
{
int number;
for ( number = smallest; number <= largest; number++ )
{
print_times_table( number, 1, 10 );
cout << «n»;
}
}
void print_times_table( int value, int lower, int upper )
{
int multiplier;
for ( multiplier = lower; multiplier <= upper; multiplier++ )
{
cout << value << » x » << multiplier << » = «;
cout << value * multiplier << «n»;
}
}
программа 4.4.
В первоначальном варианте программы
«
таблица умножения
» (
программа
4.2)
есть вложенные циклы
.
В последующих вариантах программы читабельность исход
—
ного текста улучшается с помощью процедурной абстракции
.
Преобразование тела
цикла в вызов функции позволяет производить разработку этого алгоритма независи
—
мо от остальной части программы
.
Поэтому уменьшается вероятность ошибок
,
свя
—
занных с областью видимости переменных и перегрузкой имен переменных
.
Недостаток выноса тела цикла в отдельную функцию заключается в уменьше
—
нии быстродействия
,
поскольку на вызов функции тратится больше времени
,
чем на
итерацию цикла
.
Если цикл выполняется не очень часто и не содержит большого ко
—
личества итераций
(
больше нескольких десятков
),
то временными затратами на вызов
функции вполне можно пренебречь
.
Тип данных
«
bool
»
предназначен для использования в логических выражениях
и в качестве возвращаемого значения логических функций
.
Такие функции можно
применять в качестве условий в условных операторах и операторах циклов
.
В Си
++
есть три варианта циклов
: «
for
«, «
while
»
и
«
do … while
«.
Вложенные операторы
«
if
»
в некоторых случаях можно заменить оператором
множественного ветвления
«
switch
«.
Внутри составного оператора
(
блока
),
ограниченного _______фигурными скобка
—
ми
«
{}
«,
допускается описание локальных переменных
(
внутренних переменных бло
—
ка
).
60
Упражнение 1
Разработайте функцию
,
которая принимает целочисленный параметр и возвра
—
щает логическое
(«
bool
«)
«
true
«,
только если переданное ей число является
простым числом из диапазона от
1
до
1000 (
число
1
простым не считается
).
Проверьте
свою функцию на различных входных данных с помощью тестовой программы
.
Упражнение 2
Напишите функцию
«
print_pyramid(…)
«,
которая получает целочисленный
параметр
«
height
(
высота
)»
и отображает на экране
«
пирамиду
»
заданной высоты из
символов
«
*
«.
Проверьте функцию с помощью простой тестовой программы
,
которая
должна воспроизводить следующий примерный диалог с пользователем
:
Эта программа печатает на экране «пирамиду»
заданной высоты.
Введите высоту пирамиды:
37
Введите другое
6
**
****
******
********
**********
************
Упражнение 3
Цикл
«
for
»
всегда можно переписать в форме цикла
«
while
«,
и наоборот
.
Яв
—
ляются ли две показанных ниже программы эквивалентными
?
Какие сообщения они
печатают на экране
?
Объясните свой ответ и проверьте его опытным путем
.
программа
3
а
:
#include <iostream.h>
int main()
{
int count = 1;
for ( ; count <= 5; count++ )
{
int count = 1;
cout << count << «n»;
}
return 0;
}
программа
3b:
#include <iostream.h>
int main()
{
int count = 1;
while ( count <= 5 )
{
int count = 1;
cout << count << «n»;
count++;
}
return 0;
}
61
Упражнение 4
Приведенная ниже программа должна печатать время закрытия магазина в раз
—
личные дни недели
(
в виде таблицы
).
В программе объявлен новый перечислимый
тип данных
«
день
»
и определена функция
«
closing_time(…)
«,
которая должна воз
—
вращать час закрытия магазина в заданный день
(
пока эта функция не слишком слож
—
на
–
для любого дня возвращает значение
17).
Программа демонстрирует
,
как можно
использовать типы
«
int
»
и
«
D
ay
»
в преобразованиях типов
(
в заголовке цикла
«
for
»
и
при вызове функции
«
closing_time(…)
«).
#include <iostream.h>
enum Day { Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday, Sunday };
int closing_time( Day day_of_the_week );
// Главная функция
int main()
{
int count;
// Печать заголовка таблицы
cout.width(17);
cout << «день«;
cout.width(19);
cout << «время ЗАКРЫТИЯ nn»;
// Печать таблицы от понедельника (Monday) до
// воскресенья
___________
(Sunday)
for ( count = int(Monday); count <= int(Sunday); count++ )
{
cout.width(19);
switch ( count )
{
case 0 : cout << «Понедельник»; break;
case 1 : cout << «Вторник»;
break;
case 2 : cout << «Среда»; break;
case 3 : cout << «Четверг»; break;
case 4 : cout << «Пятница»; break;
case 5 : cout << «Суббота»; break;
case 6 : cout << «Воскресенье»; break;
default : cout << «ОШИБКА!»;
}
cout.width(9);
cout << closing_time( Day(count) ) << «:00n»;
}
return 0;
}
// Конец главной функции
// Функция, возвращающая время закрытия магазина
// в заданный день недели
int closing_time( Day day_of_the_week )
{
return 17;
}
62
(
а
)
Что произойдет
(
и почему
),
если заменить оператор
«
switch
»
на строку
cout << Day(count);
?
Вместо этого замените
«
switch
»
на строку
print_day( Day(count), cout );
и добавьте описание и определение функции
«
print_day(…)
«,
внутри которой
разместите удаленный из главной функции оператор
«
switch
» (
поток стандарт
—
ного вывода
«
cout
»
передавайте в функцию как параметр по ссылке ти
—
па
«
ostream&
«).
(
б
)
магазин закрывается в воскресенье в
13:00,
в субботу в
17:00,
в среду в
20:00
и в
остальные дни в
18:00.
С помощью оператора
«
switch
»
внесите соответствующие
изменения в функцию
«
closing_time(…)
»
и проверьте работу программы
.
Упражнение 5
Напишите программу
,
которая отображает в виде таблицы количество строч
—
ных английских букв
(
от
‘a’
до
‘z’
)
в собственном исходном файле
«
ex5_5.cpp
«
(
сохраните исходный текст программы именно в этом файле
).
При разработке программы предположите
,
что у компьютера очень мало памя
—
ти
–
используйте только одну переменную типа
«
ifstream
«,
одну переменную типа
«
char
»
и две переменных типа
«
int
«.
программа должна выдавать на экран сообще
—
ния
,
похожие на следующие
:
СИМВОЛ количество ВХОЖДЕНИЙ
a 38
b 5
c 35
d 7
e 58
f 8
…
…
w 4
x 4
y 0
z 1
63
ЛЕКЦИЯ 6. Массивы и символьные строки
В программировании часто возникают задачи
,
связанные с обработкой боль
—
ших объемов данных
.
Для постоянного хранения этих данных удобно пользоваться
файлами
.
например
,
в программе для ввода и сортировки длинных числовых списков
данные можно ввести с клавиатуры один раз и сохранить в файле для последующего
многократного использования
.
Но до сих пор не было рассмотрено удобного способа
представления больших объемов данных внутри программ
.
Для этой цели в Си
++
часто применяются
–
простейшая разновидность
(
о более сложных структурах данных будет говориться в следующих лекциях
).
Массив
–
это набор переменных одного типа
(«
int
«, «
char
»
и др
.).
При объявле
—
нии массива компилятор выделяет для него
ячеек памяти
,
для
обращения к которым в программе применяется одно и то же имя
.
В то же время мас
—
сив позволяет получить прямой доступ к своим отдельным элементам
.
1.1 Объявление массивов
Оператор описания массива имеет следующий синтаксис
:
<тип данных> <имя переменной>[<целое значение>];
Допустим
,
в программе требуется обрабатывать данные о количестве часов
,
от
—
работанных в течении недели группой из
6-
ти сотрудников
.
Для хранения этих дан
—
ных можно объявить массив
:
int hours[6];
или
,
лучше
,
задать численность группы с помощью специальной константы
:
const int NO_OF_EMPLOYEES = 6;
int hours[NO_OF_EMPLOYEES];
Если подобные массивы будут часто встречаться в программе
,
то целесообраз
—
но определить новый тип
:
const int NO_OF_EMPLOYEES = 6;
typedef int Hours_array[NO_OF_EMPLOYEES];
Hours_array hours;
Hours_array hours_week_two;
В любом из трех перечисленных вариантов
,
в программе будет объявлен мас
—
сив из
6
элементов типа
«
int
«,
к которым можно обращаться с помощью имен
:
hours[0] hours[1] hours[2] hours[3] hours[4] hours[5]
Каждое из этих имен является именем
массива
.
Числа
0
,
…
,
5
назы
—
ваются
элементов
.
Отличительная особенность массива заключается в том
,
что его элементы
–
однотипные переменные
–
занимают в памяти компьютера после
—
довательные ячейки памяти
(
рис
. 1).
64
Рис. 1.
.
Расположение элементов массива в оперативной памяти
(
направление
сверху вниз соответствует возрастанию адресов ячеек памяти
).
1.2 Использование элементов массивов в выражениях
С элементами объявленного массива можно выполнять все действия
,
допусти
—
мые для обычных переменных этого типа
(
выше был приведен пример целочисленно
—
го массива
,
т
.
е
.
типа
«
int
«).
например
,
возможны операторы присваивания наподо
—
бие
:
hours[4] = 34;
hours[5] = hours[4]/2;
или логические выражения с участием элементов массива
:
if (number < 4 && hours[number] >= 40) { …
Присвоить значения набору элементов массива часто бывает удобно с помо
—
щью циклов
«
for
»
или
«
while
«.
В программе
1.1
в цикле у оператора запрашивается
количество часов
,
отработанных каждым сотрудником группы за неделю
.
Хотя более
естественной может показаться нумерация сотрудников от
1
до
6,
а не от
0
до
5,
но
необходимо помнить
,
что индексация массивов в Си
++
начинается с
0.
Поэтому про
—
грамма
1.1
вычитает
1
из порядкового номера сотрудника
,
чтобы вычислить индекс
соответствующего элемента массива
.
#include <iostream.h>
const int NO_OF_EMPLOYEES = 6;
typedef int Hours_array[NO_OF_EMPLOYEES];
int main()
{
Hours_array hours;
int count;
for ( count = 1; count <= NO_OF_EMPLOYEES; count++ )
{
cout << «сколько часов отработал сотрудник»;
cout << » номер «
<< count << «?: «;
cin >> hours[count — 1];
}
return 0;
}
программа 1.1.
В типичном сеансе работы программа
1.1
выведет на экран подобные сообщения
:
сколько часов отработал сотрудник номер
1?:
38
Сколько часов отработал сотрудник номер
2?:
42
сколько часов отработал сотрудник номер
3?:
29
Сколько часов отработал сотрудник номер
4?:
35
65
сколько часов отработал сотрудник номер
5?:
38
Сколько часов отработал сотрудник номер
6?:
37
На рис
. 2.
показано состояние целочисленного массива после ввода этих данных
.
Рис. 2.
.
состояние массива после присвоения значений его элементам
.
Полезно разобраться
,
что произошло бы
,
если бы в программе
1.1
внутри цикла
«
for
»
в операторе
«
cin …
»
отсутствовало бы вычитание
1
из переменной
«
count
«.
Компилятор Си
++ (
в отличие
,
например
,
от Паскаля
)
не обнаруживает ошибки выхо
—
да за пределы массива
,
поэтому участок памяти компьютера с массивом и сразу за
ним оказался бы в состоянии
,
показанном на рис
. 3.
Рис. 3.
.
Ошибка выхода за пределы массива
.
Другими словами
,
«
37
»
было бы размещено в ячейке памяти
,
доста
—
точной для хранения целого числа
,
которая расположена сразу после массива
«
hours
«.
Это чрезвычайно нежелательная ситуация
,
потому что компилятор может зарезерви
—
ровать эту ячейку памяти для другой переменной
(
например
,
для переменной
«
count
«).
Массивы могут быть любого типа
,
не обязательно типа
«
int
«.
Ниже приведена
программа
1.2,
в которой символьный
(«
char
«)
массив применяется для печати собст
—
венного исходного файла на экране в обратном порядке
.
#include <iostream.h>
#include <fstream.h>
const int MAX_LEN = 1000;
typedef char File_array[MAX_LEN];
int main()
{
char character;
File_array file;
int count;
ifstream in_stream;
in_stream.open(«prg6_1_2.cpp»);
in_stream.get(character);
for ( count = 0; !in_stream.eof() && count < MAX_LEN; count++ )
{
file[count] = character;
66
in_stream.get(character);
}
in_stream.close();
while (count > 0)
cout << file[—count];
return 0;
}
программа 1.2.
В заголовке цикла
«
for
»
обратите внимание на условие
«
… && count < MAX ;
…
«,
специально предусмотренное для предотвращения выхода за пределы массива
.
Чтобы у программ была понятная человеку структура
,
допускающая модифи
—
кацию и повторное использование исходного текста
,
отдельные алгоритмы следует
реализовывать в виде самостоятельных функций
.
Т
.
к
.
для хранения данных часто
применяются массивы
,
то бывают необходимы и функции для их обработки
.
Этим
функциям массивы можно передавать в качестве параметров
.
В показанном ниже
фрагменте программы
2.1
содержится определение функции
,
которая принимает мас
—
сив типа
«
Hours_array
» (
см
.
программу
1.1)
и возвращает среднее количество часов
,
отработанных сотрудниками группы
.
float average( Hours_array hrs )
{
float total = 0;
int count;
for ( count = 0; count < NO_OF_EMPLOYEES; count++ )
total += float(hrs[count]);
return ( total/NO_OF_EMPLOYEES );
}
Фрагмент программы 2.1.
Эту функцию можно сделать более универсальной
,
если размер массива не
фиксировать в ее определении
,
а передать в качестве второго параметра
:
float average( int list[], int length )
{
float total = 0;
int count;
for ( count = 0 ; count < length; count++ )
total += float(list[count]);
return ( total/length );
}
В этом примере показан очень распространенный способ оформления функций
,
работающих с массивами
:
в одном параметре передается длина массива
,
а в другом
–
сам массив
,
причем в описании параметра
—
массива не указывается длина массива
(
на
—
пример
, «
int list[]
«).
Параметры
—
массивы всегда передаются по ссылке
(
а не по значению
),
хотя при
их описании в заголовке функции символ
«
&
»
не указывается
.
правило
«
массивы все
—
67
гда передаются по ссылке
»
введено для того
,
чтобы функции не делали собственных
внутренних копий переданных им массивов
–
для больших массивов это могло бы
привести к нерациональным затратам памяти
.
Следовательно
,
как и параметры по
ссылке
,
рассматривавшиеся в
3-
ей лекции
,
все изменения элементов параметров
—
массивов внутри функций будут видны и в вызывающей функции
.
Далее показана функция
(
фрагмент программы
2.2),
принимающая в качестве
параметров три массива
.
После ее вызова каждый элемент третьего массива будет ра
—
вен сумме двух соответствующих элементов первого и второго массивов
.
void add_lists( int first[], int second[], int total[],
int length )
{
int count;
for ( count = 0; count < length; count++ )
total[count] = first[count] + second[count];
}
Фрагмент программы 2.2.
В целях безопасности
,
для защиты от случайного изменения элементов масси
—
ва
,
в описание первых двух параметров функции добавим модификатор типа
«
const
«:
void add_lists( const int fst[], const int snd[], int tot[],
int len )
{
int count;
for ( count = 0; count < len; count++ )
tot[count] = fst[count] + snd[count];
}
Теперь компилятор не будет обрабатывать ни один оператор в определении
функции
,
который пытается модифицировать элементы константных массивов
«
fst
«
или
«
snd
«.
Фактически
,
ограничение
,
накладываемое модификатором
«
const
»
в дан
—
ном контексте
,
в некоторых ситуациях оказывается слишком строгим
.
Например
,
компилятор выдаст ошибку при обработке следующих двух функций
:
void no_effect( const int list[] )
{
do_nothing( list );
}
void do_nothing( int list[] )
{
;
}
Фрагмент программы 2.3.
Ошибка компиляции программы
2.3
объясняется тем
,
что
,
хотя фактически
функция
«
do_nothing(…)
»
не выполняет никаких действий
,
но в ее заголовке отсут
—
ствует модификатор
«
const
«.
Когда компилятор в теле функции
«
no_effect(…)
«
встретит вызов функции
«
do_nothing(…)
«,
то он обратится только к заголовку функ
—
68
ции
«
do_nothing(…)
«,
и выдаст ошибку о невозможности передачи константного
массива в эту функцию
.
По отношению к массивам часто возникает задача сортировки по убыванию
или возрастанию
.
Разработано много различных алгоритмов сортировки
,
например
,
и
.
В этом параграфе кратко рассматри
—
вается один из простейших алгоритмов сортировки
–
.
Основные действия алгоритма для сортировки массива длиной
заключаются
в следующем
:
Для каждого значения индекса
выполнить два действия
:
1)
Среди элементов с индексами от
до
(
-1)
найти
элемент с минимальным значением
.
2)
Обменять значения
—
го и минимального элемен
—
тов
.
Работу этого алгоритма рассмотрим на примере сортировки массива из
5
целых
чисел
:
int a[5];
значения элементов неотсортированного массива показаны на рис
. 4.
Рис. 4.
.
Начальное состояние массива
.
В процессе сортировки методом выбора наименьшего элемента массив будет
последовательно переходить в состояния
,
показанные на рис
. 5 (
слева направо
).
Каж
—
дое состояние получается из предыдущего путем перестановки двух элементов
,
поме
—
ченных на рис
. 5
кружками
.
Рис. 5.
.
Последовательные шаги сортировки массива методом выбора наи
—
меньшего элемента
.
На Си
++
алгоритм сортировки можно реализовать в виде трех функций
.
Функ
—
ция высокого уровня будет называться
«
selection_sort(…)
» (
у нее два параметра
–
сортируемый массив и его длина
).
Сначала эта функция вызывает вспомогательную
функцию
«
minimum_from(array,position,length)
«,
которая возвращает индекс мини
—
мального элемента массива
«
array
«,
расположенного в диапазоне между индексом
«
position
»
и концом массива
.
Затем для обмена двух элементов массива вызывается
функция
«
swap(…)
«.
void selection_sort( int a[], int length )
69
{
for ( int count = 0; count < length — 1; count++ )
swap( a[count], a[minimum_from(a,count,length)] );
}
int minimum_from( int a[], int position, int length )
{
int min_index = position;
for ( int count = position + 1; count < length; count++ )
if ( a[count] < a[min_index] )
min_index = count;
return min_index;
}
void swap( int& first, int& second )
{
int temp = first;
first = second;
second = temp;
}
Фрагмент программы 3.1.
Массивы в Си
++
могут иметь более одной размерности
.
В данном параграфе
кратко описаны двумерные массивы
.
Они широко применяются для хранения дву
—
мерных структур данных
,
например
,
растровых изображений и матриц
.
При обработке изображений часто используются битовые маски
–
вспомога
—
тельные двуцветные
(
черно
—
белые
)
изображения
,
состоящие только из
0 (
белая точка
)
и
1 (
черная точка
) (
или
,
логических значений
«
false
»
и
«
true
«).
Предположим
,
что
требуется маска размером
64
х
32
пиксела
.
Для описания соответствующего массива
возможны операторы
:
const int BITMAP_HEIGHT = 64;
const int BITMAP_WIDTH = 32;
bool bitmap[BITMAP_HEIGHT][BITMAP_WIDTH];
При обращении к элементам массива
«
bitmap
»
необходимо указывать два ин
—
декса
.
например
,
чтобы изменить значение
2-
го пиксела слева в
4-
й строке надо запи
—
сать оператор
:
bitmap[3][1] = true;
Все сказанное во
2-
м параграфе относительно одномерных массивов как пара
—
метров функций верно и для двумерных массивов
,
но у них есть еще одна особен
—
ность
.
В прототипах и заголовках функций можно опускать первую размерность мно
—
гомерного массива
—
параметра
(
т
.
е
.
записывать пустые квадратные скобки
«
[]
«),
но все
остальные размерности надо обязательно указывать
.
Далее в качестве примера приве
—
дена функция
,
заполняющая битовую карту черными пикселами
.
void clear_bitmap( bool bitmap[][BITMAP_WIDTH],
int bitmap_height )
{
for ( int row = 0; row < bitmap_height; row++ )
70
for ( int column = 0; column < BITMAP_WIDTH; column++ )
bitmap[row][column] = false;
}
Во многих уже рассматривавшихся программах для вывода на экран часто ис
—
пользовались символьные строки
,
например
, «
«Введите возраст:»
«.
В Си
++
строки
хранятся и обрабатываются в виде символьных массивов
,
на которые накладывается
дополнительное ограничение
(
см
.
п
.5.1).
5.1 Завершающий нуль-символ ‘