МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РЕСПУБЛИКИ КАЗАХСТАН

advertisement
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РЕСПУБЛИКИ КАЗАХСТАН
ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ имени ШАКАРИМА г. СЕМЕЙ
Документ СМК 3 уровня
УМКД
УМКД 042-39. 1.ХХ/032013
УМКД
Редакция №____от_____
Учебно-методические
материалы по дисциплине
«Объектно-ориентированное
программирование на С++»
УЧЕБНО-МЕТОДИЧЕСКИИ КОМПЛЕКС
ДИСЦИПЛИНЫ
«Объектно-ориентированное программирование на С++»
для специальности 6М011100 – «Информатика»
УЧЕБНО-МЕТОДИЧЕСКИЕ МАТЕРИАЛЫ
Семей
2014
СОДЕРЖАНИЕ
1
2
3
4
Глоссарий
Лекции
Практические занятия
Самостоятельная работа магистранта
1 Глоссарий
1
Алфавит – совокупность символов, отображаемых на устройствах печати и
экранах и/или вводимых с клавиатуры терминала
2
Алгоритм – содержание и последовательность операций, четко определяющих
решение задачи путем вычислительного процесса, преобразующего исходные данные в
конечный результат
3
Блок-схема – графическое изображение алгоритма, подлежащего программированию
4
Глобальная переменная – переменная, областью определения которой является
вся программа или блок с вложенными в него подблоками
5
Жизненный цикл программного обеспечения – это весь период его разработки и
эксплуатации, начиная с момента возникновения замысла и заканчивая прекращением ее
использования.
6
Зарезервированное слово слово, смысл которого зафиксирован правилами языка и
по которому транслятор распознает основные языковые конструкции
7
Инструментарий технологии программирования – это программные продукты,
предназначенные для поддержки технологии программирования
8
Императивное программирование — это исторически первая методология
программирования, которой пользовался каждый программист, программирующий на
любом из «массовых» языков программирования – Basic, Pascal, C. Она характеризуется
принципом последовательного изменения состояния вычислителя пошаговым образом.
9
Инкапсуляция - это сокрытие информации и комбинирование данных и функций
(методов) внутри объекта.
10
Компонент – программный модуль или объект, готовый для использования в
качестве составного блока программы и которым можно визуально манипулировать во
время разработки программы
11
Лексика – совокупность правил образования цепочек символов (лексем),
образующих идентификаторы (переменные и метки), операторы, операции и другие
лексические компоненты языка
12
Линейный алгоритм – алгоритм, в котором действия осуществляются
последовательно друг за другом
13
Логический алгоритм – алгоритм, в соответствии с которым решение
поставленных задач сводится к логическим действиям
14
Логический подходу к программированию программа представляет собой
совокупность правил или логических высказываний. Кроме того, в программе допустимы
логические причинно-следственные связи, в частности, на основе операции импликации.
15
Машинный язык – язык программирования, предназначенный для представления
программ и данных в форме, пригодной для непосредственного восприятия их
устройствами вычислительной машины
16
Машинно-ориентированный язык – язык программирования, отражающий
структуру конкретного типа компьютера
17
Методология программирования – совокупность методов, применимых в
жизненном цикле программного обеспечения и объединенных общим философским
подходом.
18
Модульное программирование – способ разработки программ, при котором
относительно независимые подзадачи программируются в виде отдельных программных
модулей
19
Модульное программирование — это такой способ программирования, при
котором вся программа разбивается на группу компонентов, называемых модулями,
причем каждый из них имеет свой контролируемый размер, четкое назначение и детально
проработанный интерфейс с внешней средой.
20
Модуль — 1) это совокупность команд, к которым можно обратиться по имени;
2) это совокупность операторов программы, имеющая граничные элементы и
идентификатор (возможно агрегатный).
21
Методология структурного императивного программирования — подход,
заключающийся в задании хорошей топологии императивных программ, в том числе
отказе от использования глобальных данных и оператора безусловного перехода,
разработке модулей с сильной связностью и обеспечении их независимости от других
модулей.
22
Методология ООП использует метод объектной декомпозиции, согласно
которому структура системы (статическая составляющая) описывается в терминах
объектов и связей между ними, а поведение системы (динамическая составляющая) - в
терминах обмена сообщениями между объектами. Сообщения могут быть как реакцией на
события, вызываемые как внешними факторами, так и порождаемые самими объектами.
23
Наследование Построение иерархии порожденных объектов с возможностью для
каждого такого объекта-наследника доступа к коду и данным всех порождающих
объектов-предков.
24
Нисходящее программирование – способ разработки программ, при котором
программирование ведется методом «сверху-вниз», от общего к деталям
25
Отладка программы – этап разработки программы, состоящий в локализации,
выявлении и устранении программных ошибок, факт существования которых уже
установлен
26
Отладчик – программа, облегчающая программисту выполнение отладки
разрабатываемых им программ
27
Полиморфизм (полиморфизм включения) — присваивание действию одного
имени, которое затем разделяется вверх и вниз по иерархии объектов, причем каждый
объект иерархии выполняет это действие способом, подходящим именно ему.
28
Проблемно-ориентированный язык – язык программирования, предназначенный
для решения задач определенного класса
29
Программа – 1) последовательность указаний (команд или описаний и
операторов), задающая алгоритм вычислительной машине
2) завершенный продукт, пригодный для запуска своим автором на системе, на которой он
был разработан.
30
Программирование – процесс создания программы
31
Программный продукт — программа, которую любой человек может запускать,
тестировать, исправлять и развивать.
32
Программный комплекс — набор взаимодействующих программ, согласованных
по функциям и форматам, точно определенным интерфейсам, и вкупе составляющих
полное средство для решения больших задач.
33
Процедура – поименованная часть программы, которая может выполнять
некоторые четко заданные действия над условными данными, определяемыми с помощью
формальных параметров
34
Процедурно-ориентированные языки – это языки, в которых имеется
возможность описания программы как совокупности процедур или подпрограмм
35
Разветвляющийся алгоритм – алгоритм, в котором действие выполняется по
одной из возможных ветвей решения задачи, в зависимости от выполнения условий
36
Семантика – смысловое содержание конструкций, предложений языка,
семантический анализ – это проверка смысловой правильности конструкции
37
Семантический анализ – выявление несоответствий типов и структур
переменных, функций и процедур
38
Семантическая ошибка – ошибка в программе, не связанная с нарушением
синтаксиса языка программирования (неправильное описание алгоритма решения задачи,
неверное определение типа или значения переменных, несогласованность исходных
данных с алгоритмом, неверное использование правильных синтаксических конструкций
языка)
39
Синтаксис – совокупность правил образования языковых конструкций, или
предложений языка программирования – блоков, процедур, составных операторов,
условных операторов, операторов цикла и пр.
40
Синтаксическая
конструкция
–
допустимое
синтаксисом
языка
программирования сочетание символов или символов и зарезервированных слов
41
Синтаксический анализ – проверка правильности конструкций, использованных
программистом при подготовке текста
42
Система программирования – совокупность средств разработки программ (языки
программирования, текстовые редакторы, трансляторы, редакторы связей, библиотеки
подпрограмм, утилиты и обслуживающие программы), обеспечивающих автоматизацию
составления и отладки программ, подготовку соответствующей документации
43
Синтаксическая ошибка – ошибка в программе, связанная с нарушением
синтаксиса языка программирования (неправильное написание констант, переменных,
выражений, операторов, процедур и других синтаксических конструкций)
44
Структура данных – способ объединения, взаимосвязь или взаимное
расположение нескольких элементов данных, рассматриваемых как одно целое
45
Структурное
программирование
–
методология
программирования,
направленная на создание логически простых и понятных программ. Структурное
программирование основано на предположении, что логичность и понятность программы
облегчает разработку, доказательство правильности и последующее сопровождение
программы, а также обеспечивает ее надежность
46
Стадия — часть действий по созданию программного обеспечения, ограниченная
некоторыми временными рамками и заканчивающаяся выпуском конкретного продукта,
определяемого заданными для данной стадии требованиями.
47
Технология программирования изучает технологические процессы и порядок их
прохождения – стадии (с использованием знаний, методов и средств).
48
Технологический подход определяется спецификой комбинации стадий и
процессов, ориентированной на разные классы программного обеспечения и на
особенности коллектива разработчиков.
49
Тестирование программы – процесс выполнения программы с целью
обнаружения ошибок путем реального выполнения специально подобранных контрольных примеров
50
Технология программирования – технология разработки программного средства
как совокупность абсолютно всех технологических процессов его создания – от момента
зарождения идеи о данном программном средстве до составления необходимой
программной документации
51
Трассировка программы – выполнение программы или ее участка,
сопровождающееся выводом на экран, принтер или другой регистрацией в
хронологической последовательности информации о событиях, связанных с выполнением
программы
52
Функция – поименованная часть программы, результатом выполнения которой
является значение, присваиваемое имени функции
53
Важнейшей характеристикой функционального подхода является то
обстоятельство, что всякая программа, разработанная на языке функционального
программирования, может рассматриваться как функция, аргументы которой, возможно,
также являются функциями.
54
Цикл с постусловием – цикл, у которого тело расположено перед проверкой
условия
55
Цикл с предусловием – цикл, у которого тело расположено после проверки
условий
56
Циклический алгоритм – алгоритм, в котором некоторая часть операций (тело
цикла – последовательность команд) выполняется многократно
57
Язык программирования – это формализованный язык для описания алгоритма
решения задач на компьютере
Лекция №1. Введение в С++ программирование.
1. Основные понятия.
2. Типичная среда С++ программирования.
3. Функции ввода - вывода в С++.
4. Использование <iostream> и <stdio.h> .
5. Способ руссификации.
В наш век кажется нет такой отрасли знаний, которая бы так стремительно
развивалась, как программирование и вычислительная техника. Кажется, никакая еще
наука не развивалась такими семимильными шагами и такими темпами. Возникает новая
техника: компьютеры, процессоры, дисководы. Появляются новые возможности и новые
информационные технологии.
Программирование сейчас везде и всюду. Оно обслуживает предприятия, офисы,
конторы, учебные заведения - все, где есть управленческий труд и потоки информации.
Нелегок труд программиста. Трудны языки программирования. Особенно поражает их
многообразие. И сам процесс программирования становится таким объемным и сложным,
что старые методы уже никого не удовлетворяют, и на смену им приходят новые методы и
новые языки программирования, подобные языку С++ и системе Visual C++ 6.0,
способные убыстрить во много раз разработку и сопровождение программ. Сегодня мы
смотрим назад из XXI-ого века в XX-й век и восхищаемся новейшими Windowsтехнологиями, визуальным подходом и объектно ориентированным программированием.
За короткий срок они покорили и завоевали весь мир.
Немаловажную роль здесь играет язык программирования C++. Но зачем он был
нужен, как и почему возник и был востребован? На эти и на другие вопросы мы и
постараемся вместе с Вами найти правильные ответы на этом уроке, посвященном С++.
C++ - расширение языка С - был разработан сотрудником научноисследовательского центра AT&T Bell Laboratories (Нью-Джерси, США) Бьерном
Строустропом в 1979 году. С++ содержит в себе все, что есть в С. Но, кроме того, он
поддерживает объектно ориентированное программирование (Object Oriented
Programming, OOP). Изначально С++ был создан для того, чтобы облегчить разработку
больших программ. Объектно ориентированное программирование это новый подход к
созданию программ.
В 60-е годы XX века особо остро возникла потребность создавать большие и
сложные программы. Однако, она натолкнулась на ряд трудностей. Люди, связанные с
разработкой программ, начали понимать, что создание сложных программ - это гораздо
более сложная задача, чем они себе представляли. Проведенные в этот период
исследования привели к появлению и интенсивному развитию структурного
программирования. Этот подход отличался большей дисциплинированностью, ясностью и
простотой тестирования и отладки программ, легкостью их модификации.
Создание в 1971 году Никлаусом Виртом (швейцарским математиком) языка
Паскаль было одним из замечательных результатов проводившихся исследований в
ученой унивеситетской среде. Созданный первоначально исключительно для изучения
структурного программирования в академической среде, он стал наиболее
предпочитаемым языком во многих унивестиетах мира. Однако, отсутствие в нем
необходимых свойств для решения коммерческих задач сдерживало его применение в
коммерции, в промышленности и управлении.
В течение 70-х и в начале 80-х годов при огромной заинтересованности и поддержке
Министерства Обороны США был создан язык программирования Ада. Министерством
Обороны США использовались сотни отдельных языков. Но все время хотелось иметь
один язык, который бы удовлетворял всем интересам этого ведомства. Таким языком был
выбран Паскаль. Но в итоге разработки язык Ада оказался совсем не похожим на Паскаль.
Наиболее важное свойство Ады - многозадачность. Оно позволяет программистам
разрабатывать
алгоритмы
параллельного
выполнения
действий.
Другие языки, как например С и С++, одновременно могли выполнять одно действие.
2. Типичная среда С++ программирования.
Современные системы программирования на С++ состоят из нескольких составных
частей. Это такие части, как сама среда программирования, язык, стандартная библиотека
С-функций и различные библиотеки С-классов.
Как правило, чтобы выполнить программу на С++, необходимо пройти через 6
этапов: редактирование, препроцессорную (то есть предварительную) обработку,
компиляцию, компоновку, загрузку и выполнение. Мы с Вами остановим свое внимание
на системе С++ программирования Borland C++ v. 5.0 или 5.2.
Первый этап представляет создание и редактирование файла с исходныи текстом
программы. Он может выполняться с помощью простейшего редактора текстов программ.
Программист набирает в этом редакторе свою С++ программу. При необходимости он
снова обращается к ней и вности с помощью этого редактора изменения в исходный текст
программы. Далее программа запоминается на диске. Имена файлов С/C++ программ
оканчиваются на "c" или "cpp". Однако, пакет программ Borland C++ v 5.0 (5.2) имеет
встроенный редактор, которым также можно пользоваться. На уроках, посвященных Спрограммированию, мы уже подробно говорили об этом этапе и, я думаю, все умеют его
выполнять.
На втором этапе компилятор начинает препоцессорную обработку текста программы
прежде чем ее компилировать. Компилятор. Что он делает? Он переводит программу в
машинный
код.
То
есть
это
объектный
код
программы.
Следует знать, что в системе С++ программирования перед началом этапа самой
трансляции всегда выполняется программа предварительной обработки. Что она делает?
Она отыскивает так называемые "директивы трансляции" или "директивы препроцессора",
которые указывают, какие нужно выполнить преобразования перед трансляцией
исходного текста программы. Обычно это включение других текстовых файлов в файл,
который подлежит компиляции. Препроцессорная обработка инициируется компилятором
перед тем, как программа будет преобразована в машинный код. Это позволяет забирать
нужные программы-функции в текст компилируемой программы до начала процесса
компоновки.
Третий этап это компиляция. Как правило, программы на языке С++ содержат ссылки
на различные функции, которые определены вне самой программы. Например, в
стандартных библиотеках или в личных библиотеках программистов. Объкетный код,
созданный компилятором содержит "дыры" на месте этих отсутствующих частей.
Четвертый этап - компоновка. Компоновщик связывает объектный код с кодами
отсутствующих функций и создает таким оьразом исполняемый загрузочный модуль (без
пропущенных "дыр").
Пятый этап - загрузка. Перед выполнением программа должна быть размещена в
памяти. Это делается с помощью загрузчика, который забирает загрузочный модуль
программы с диска и перемещает его в память.
Наконец шестой этап - это выполнение. Программа редко заработает с первой
попытки. Каждый из названных этапов может заканчиваться ошибкой или неудачей из-за
ошибки.
Тогда программист должен вернуться к редактированию исходного текста
программы. Он должен внести необходимые изменения в текст программы,
предварительно его хорошо проанализировав. Затем снова пройти через все этапы работы
с исходным текстом программы до получеия работающего без ошибок загрузочного
модуля.
3. Функции ввода - вывода в С++.
Функция "cin" ( the standart input stream - стандартный поток ввода ) в С++
читается как "си-ин". Обычно она выполняет ввод с клавиатуры. Хотя "cin" может быть
связана и с другим устройством.
Функция "cout" ( the standart output stream - стандартный поток вывода ) в С++ читается
как "Си-аут". обычно она выполняет вывод на экран дисплея. Однако, "cout" может быть
связана и с другим устройством. Когда говорят, что "программа печатает результат", то
обычно подразумевают, что данные отображаются на экране. Вместе с тем результаты
могут быть выведены и на другое устройство, например, на диски или на принтер в виде
твердой копии на бумаге.
Необходимо помнить, что существует еще стандартный поток ошибок: "the
standart error stream", который обозначается как cerr. Этот поток обычно связывается с
экраном.
Часто программисты и пользователи направляют поток "cout" на другие устройства,
сохраняя экран для потока "cerr" ошибок, чтобы иметь возможность немедленно получать
сведения об ошибках в программе в процессе ее работы.
4. Использование <iostream> и <stdio.h> .
C - достаточно простой язык. Макросы, указатели, структуры, массивы и функции
- это все, чем он на самом деле располагает. В С можно всегда реализовать алгоритм
любой сложности. Каким бы сложным он не оказался. Для этого вполне хватает
перечисленного
набора
средств.
В С++ все обстоит иначе. Наравне с макросами, указателями, структурами, массивами и
функциями здесь используются закрытые и защищенные члены классов, перегрузка
функций, аргументы по умолчанию, конструкторы и деструкторы, операции,
определяемые пользователем, встроенные функции, ссылки, дружественные классы и
функции, шаблоны, исключения, пространства имен и так далее. Такие богатые средства
проектирования дают программисту очень широкие возможности для программирования.
Это же, в свою очередь, требует существенно иной культуры программирования от
программиста.
Столкнувись с таким широким выбором, многие С-программисты теряются и
продолжают держаться за то, к чему они привыкли. В этом нет ничего предосудительного,
однако, при этом теряется дух языка С++. От таких С-привычек необходимо будет просто
избавляться, как бы это не было трудно или мучительно! В частности при переходе от С к
С++ необходимо выполнять ПРАВИЛО:
Предпочитайте <iostream> использованию <stdio.h>.
Операторы "scanf" и "printf" и им подобные, используемые в Си, далеки от совершенства.
Они, к примеру, не выполняют контроля типа данных. Контроль же типов данных и
расширяемость - краеугольные камни С++. Поэтому лучше всего с самого начала
опираться на эти возможности в своих программах. Кроме того, эти операторы ptintf/scanf
отделяют переменные, которые необходимо прочитать или записать от форматирующей
информации, управляющей записью и чтением. Это делается так же, как в языке
FORTRAN. Но эта методика уже устарела, и пора распроститься с ней.
Однако все же в ряде случаев использование старого обращения <stdio> более
предпочтительно. Все это еще будет нами обсуждаться далее по ходу изучения языка С++.
5. Способ руссификации.
Воспользуемся следующим шаблоном программы для руссификации сообщений в
редакторе компилятора и на экране решения.
#include <iostream.h> // для cout
#include <windows.h> // для CharToOem
int main ()
{
char str[20];
CharToOem("Русский тест", str);
cout << str;
}
Напишем простую программу, которая бы выдавала на экран: фамилию, имя, отчество,
домашний телефон и адрес владельца телефона.
#include <iostream.h> // для cout
#include <windows.h> // для CharToOem
#include <conio.h>
main()
{
long int delay;
char str[50];
textbackground(4);
textcolor(15);
clrscr();
CharToOem("Сапожников Георгий Иванович \n", str);
cout << str;
CharToOem("Домашний телефон в г. Краснодаре: 9-77-99-22 \n", str);
cout << str;
CharToOem("ул. Мастера Пипкина, дом 824, кв. 936 \n", str);
cout << str;
for (delay=1; delay<=1999999999; delay++);
}
Здесь на рисунках Вы видите применение функции CharToOem для вывода информации
на русском языке на экран компьютера.
На этом наш первый урок закончен. На этом на сегодня все.
Рекомендуемая литература:
1.
Холзнер С. Visual C++ 6: учебный курс - СПб: Питер,2001. - 576 с. : ил.
2.
Тихомиров Ю. Visual C++ 6 - Спб.:БХВ - Санкт-Петербург, 1998. - 496
с. : ил.
3.
Дейтел Х., Дейтел П. Как программировать на С++: Пер. с англ. - М,:
Издательство БИНОМ, 1998 - 1024 с.: ил.
4.
Шилдт, Герберт. Полный справочник по С, 4-е издание. : Пер. с англ. -
М,: Издательский дом "Вильямс", 2002. - 704 с. : ил. - Парал.т ит. англ.
5.
Шилдт, Герберт. Самоучитель С++, 3-е издание: пер. с англ. - СПб.:
BVH - Санкт-Петербург, 1998.-688 с.
6.
Павловская Т.А. С/C++. Программирование на языке высокого уровня. /
Т.А. Павловская. - СПб.: Питер, 2002. - 464 с.: ил.
7.
Культин Н.Б. C/C++ в задачах и примерах. - СПб.:БХВ-Петербург, 2001.
- 288 с.: ил.
8.
Березин Б.И., Березин С.Б. Начальный курс С и С++. - М,:
ДИАЛОГ_МИФИ, 1996. - 288 с.
9.
Подбельский В.В., Фомин С.С. Программирование на языке Си: Учеб.
пособие. - М.: Финансы и статистика, 1998. - 600 с.: ил.
10.
Франка П. С++: учебный курс. - СПб.: Питер, 2001. - 528 с.: ил.
11.
Дэвис Стефан Р. С++ для "чайников", 4-е издание.: Перев. с англ.: Уч.
пос. - М.: Издательский дом "Вильямс", 2001. - 336 с.: ил.
12.
Джонс Р., Стюарт Я. Программируем на Си/Пер. с англ. и предисл. М.Л.
Сальникова, Ю.В. Сальниковой. - М.: Компьютер, ЮНИТИ, 1994. - 236 с.: ил.
13.
Скляров В.А. Программирование на языках Си и Си++: Практ. пособие.
- М.: Высш. шк., 1996. -240 с.: ил.
14.
Пашенков В.В. Язык программирования Си. - М.: Центр НТТМ
"Алгоритм", 1990. - 76 с.
15.
УинерР Р. Язык Турбо Си: Пер. с англ. -М.: Мир, 1991. - 384 с.: ил.
16.
Першиков В.И., Савинков В.М. Толковый словарь по информатике.-М.:
Финансы и статистика, 1991.-543 с.
17.
Киммел П. и др. Borland C++ 5: пер. с англ. - Санкт-Петербург, 1997.976 с., ил.
Лекция 2. Объектно ориентированное С++ программирование.
Остановимся на самом распространенном заблуждении: программирование на
С++ - это объектно ориентированное программирование. Это далеко не так.
С++ имеет механизмы для объектно ориентированного программирования и потому
поддерживает объектно ориентированный подход. Однако, личное дело программиста:
воспользоваться или нет преимуществами этой философии. Можно писать на языке С++
программы, как и прежде в С, не пользуясь объектно ориентированной добавкой С++.
Ведь С-программирование - это часть С++. Потому можно писать в С++ и очень успешно
С-программы, не пользуясь объектно ориентированной идеологией С++.
Но тогда в каком же случае и при каких обстоятельствах можно, нужно, выгодно и
имеет смысл использовать концепцию объектной ориентированности?
Как уже упоминалось, первоначально программирование для компьютеров было
преимущественно связано с решением математических задач. Программист сам работал за
пультом ЭВМ: он писал на машинном языке свои программы и затем сам же вводил
команды в память компьютера и отлаживал их без использования специальных средств
автоматизации. Естественно, этот процесс был очень трудоемким и медленным.
Элементарный выход был найден в разделении труда. На Вычислительном центре
стали повсеместно вводить должность оператора, которому поручалась работа по вводу
информации в память ЭВМ. Теперь программист мог большую часть своего рабочего
времени проводить за свои рабочим столом: думать и программировать, но он должен был
еще и готовить задание оператору. Это, конечно, несколько повысило эффективность его
труда. Но не столь резко. Программист все же предпочитал сам выходить со своей задачей
для отладки на компьютер.
В то время аппаратные средства и, в частности, память ЭВМ, были еще весьма
ограничены. Компьютерные сбои были очень частым и весьма распространенным
явлением.
Но по мере увеличения мощности оборудования и снижения его себестоимости
значительно расширялся круг задач. Росла сложность задач, решаемых на компьютере.
Возникала настоятельная потребность в ускорении процесса программирования без
потери качества программ. Значительно усложнялось и само программирование.
Возникли, как я уже упомянул, и первые проблемы в нем. Для их преодоления были
изобретены подпрограммы, библиотеки подпрограмм, а затем и первые языки
программирования:
- Fortran - для решения инженерных задач;
- Cobol - для решения экономических задач;
- Pascal - для обучения методам структурного программирования.
Были предложены новые методики в программировании, например, структурное
программирование. Это были выдающиеся попытки осмыслить проблемы организации
труда программиста и Вычислительного центра в целом. Они дали свои положительные
результаты.
Были в то время и первые попытки создавать специальные языки программирования
для начинающих. Например, таким был всем известный Basic. В момент его изобретения
авторы языка должны были ограничиться только заглавными буквами латинского
алфавита, так как реальная память не позволяла им еще использовать и строчные буквы.
Но технический прогресс в области вычислительной техники развивается такими
семимильными шагами, так стремительно и быстро, что вскоре были изобретены новые
запоминающие устройства для оперативной памяти компьютера, и память стала такой
дешевой, что поменялась идеология программирования. Теперь уже никто не настаивал и
не говорил об экономии ячеек памяти. Это перестало быть актуальным. Памяти теперь
было, что называется, навалом. Можно было разрабатывать гигантские комплексы
программ, состоящие не просто из 50000 команд, а превышающие этот рубеж в 2-3 и
более раз.
Но возможности памяти человека оставались прежними. Становилось все труднее,
следуя процедурному программированию, отслеживать поведение функций, из которых
строилась, как правило, программа. На смену процедурному подходу к
программированию пришел объектно ориентированный подход.
Были введены понятия объектов, классов и другие нововведения, которые и делают
С++ объектно ориентированным.
Таким образом, объектно ориентированное программирование позволило успешно
программировать огромные, точнее сверх большие комплексы программ, и именно в этих
случаях его применение оправдано.
2. Процедурное и объектно ориентированное программирование.
Отличительным
признаком
процедурного
программирования
является
использование подпрограмм, процедур и функций. Программисты давно поняли, что
задача решается проще, если ее разложить на подзадачи. Эти подзадачи и стали основой
разработки подпрограмм. В языке Паскаль подпрограммы - это процедуры и функции. В
языке Си роль подпрограмм выполняют функции.
В объектно ориентированном программировании идут дальше подпрограмм,
процедур и функций. Степень обобщения реального мира такова, что, как в биологии и
естествознвании, в программировании в последнее время вводятся объекты и из них
образуются классы объектов, совершенно аналогичные объектам и классам объектов в
других разделах знаний.
Например, рассмотрим задачу пополнения вклада на сберегательном счету в банке.
При процедурном подходе первое, что приходит на ум - это дробить поставленную задачу
на более мелкие самостоятельные части - подзадачи. В данном случае можно предложить
в задаче "Внести вклад" следующие подзадачи:
 сохранение первого вклада;
 рассчёт нового баланса:
1.
поиск предыдущего баланса;
2.
добавление новой суммы вклада;
3.
запоминание нового баланса.<>/LI>
Этот
подход
наглядно
изображен
ниже
на
следующем
рисунке:
На этом рисунке Вы видите "дерево" подпрограмм. Но с увеличением сложности
задачи количество "ветвей" на "дереве" возрастает. И если решаемая задача слишком
большая, то и количество подзадач становится огромным и трудноуправляемым. В таком
случае даже группе программистов будет весьма сложно осмыслить всю задачу в целом и
следить за ее развитием и реализацией отдельными программистами.
Естественно, что указать ту границу, за которой сложность процедурного подхода
становится решающим фактором, сдерживающим моментом программирования, довольно
трудно. Некоторые специалисты оценивают эту "точку фазового перехода" примерно в
100000 строк кода программы. Так из процедурного подхода возник новый объектно
ориентированный подход в программировании.
В объектно-ориентированном программировании программист уходит от такого
деления задачи на мелкие части. Он пытается увидеть задачу как некую "абстракцию"
реального мира. Его интересуют взаимодействия между этими идеализированными
"абстракциями" - объектами реального мира. Таким образом, он должен, используя свой
опыт и знания, увидеть эти "абстракции", распознать их в своей задаче. Давайте вернемся
к нашему примеру со сберкнижкой и вкладами. Какие здесь имеются реальные объекты?
Очевидно, что это счет в банке и сберегательная книжка. Как они взаимодействуют?
Когда производится первый вклад, то запись о нем вносится в сберегательную книжку.
При повторных взносах нужно вычислять новый баланс: сумма вклада должна быть
добавлена к уже имеющейся на сберкнижке. Но эта операция сложения является
неотъемлемым свойством самой сберкнижки. Вам нужно предъявить новую итоговую
сумму вклада. Если изобразить это на рисунке, то все будет выглядеть примерно так:
Таким образом, при объектно ориентированном подходе выделены два
взаимодействующих объекта: счет в сберегательном банке (SavingsAccount) и
сберегательная книжка (TransactionRegister). Обратите внимание на то, что при новом
объектно ориентированном подходе решение задачи наглядно изображено уже не
деревом, как это было прежде, а некоторым графом. На этом рисунке видно, что проект
задачи представлен в виде двух объектов. Этим объектам сопоставлены определенные
свойства, и схема задачи стала значительно проще, чем при процедурном подходе, когда
вся задача расчленялась на множество подзадач - подпрограмм.
Рекомендуемая литература:
1.
Холзнер С. Visual C++ 6: учебный курс - СПб: Питер,2001. - 576 с. : ил.
2.
Тихомиров Ю. Visual C++ 6 - Спб.:БХВ - Санкт-Петербург, 1998. - 496
с. : ил.
3.
Дейтел Х., Дейтел П. Как программировать на С++: Пер. с англ. - М,:
Издательство БИНОМ, 1998 - 1024 с.: ил.
4.
Шилдт, Герберт. Полный справочник по С, 4-е издание. : Пер. с англ. М,: Издательский дом "Вильямс", 2002. - 704 с. : ил. - Парал.т ит. англ.
5.
Шилдт, Герберт. Самоучитель С++, 3-е издание: пер. с англ. - СПб.:
BVH - Санкт-Петербург, 1998.-688 с.
6.
Павловская Т.А. С/C++. Программирование на языке высокого уровня. /
Т.А. Павловская. - СПб.: Питер, 2002. - 464 с.: ил.
7.
Культин Н.Б. C/C++ в задачах и примерах. - СПб.:БХВ-Петербург, 2001.
- 288 с.: ил.
8.
Березин Б.И., Березин С.Б. Начальный курс С и С++. - М,:
ДИАЛОГ_МИФИ, 1996. - 288 с.
9.
Подбельский В.В., Фомин С.С. Программирование на языке Си: Учеб.
пособие. - М.: Финансы и статистика, 1998. - 600 с.: ил.
10.
Франка П. С++: учебный курс. - СПб.: Питер, 2001. - 528 с.: ил.
11.
Дэвис Стефан Р. С++ для "чайников", 4-е издание.: Перев. с англ.: Уч.
пос. - М.: Издательский дом "Вильямс", 2001. - 336 с.: ил.
12.
Джонс Р., Стюарт Я. Программируем на Си/Пер. с англ. и предисл. М.Л.
Сальникова, Ю.В. Сальниковой. - М.: Компьютер, ЮНИТИ, 1994. - 236 с.: ил.
13.
Скляров В.А. Программирование на языках Си и Си++: Практ. пособие.
- М.: Высш. шк., 1996. -240 с.: ил.
14.
Пашенков В.В. Язык программирования Си. - М.: Центр НТТМ
"Алгоритм", 1990. - 76 с.
15.
УинерР Р. Язык Турбо Си: Пер. с англ. -М.: Мир, 1991. - 384 с.: ил.
16.
Першиков В.И., Савинков В.М. Толковый словарь по информатике.-М.:
Финансы и статистика, 1991.-543 с.
17.
Киммел П. и др. Borland C++ 5: пер. с англ. - Санкт-Петербург, 1997.976 с., ил.
Лекция 3. Структура программы на Си++
Программа на языке Си имеет следующую структуру:
#директивы препроцессора
.........
#директивы препроцессора
функция а ( )
операторы
функция в ( )
операторы
void main ( ) //функция, с которой начинается выполнение программы
операторы
описания
присваивания
функция
пустой оператор
составной
выбора
циклов
перехода
Директивы препроцессора - управляют преобразованием текста программы
до ее компиляции. Исходная программа, подготовленная на СИ в виде текстового файла,
проходит 3 этапа обработки:
1)
препроцессорное преобразование текста ;
2)
компиляция;
3)
компоновка (редактирование связей или сборка).
Исходный
текст (cpp)
препроцессор
Включаемые
файлы (h)
Исполняемый
код (exe)
Полный текст
программы
Компоновщик
компилятор
Объектный
код (obj)
Стандартные
библиотеки (lib)
После этих трех этапов формируется исполняемый код программы. Задача
препроцессора - преобразование текста программы до ее компиляции. Правила
препроцессорной обработки определяет программист с помощью директив
препроцессора. Директива начинается с #. Например,
1) #define - указывает правила замены в тексте.
#define ZERO 0.0
Означает , что каждое использование в программе имени ZERO будет заменяться
на 0.0.
2) #include< имя заголовочного файла> - предназначена для включения в текст
программы текста из каталога «Заголовочных файлов», поставляемых вместе со
стандартными библиотеками. Каждая библиотечная функция Си имеет соответствующее
описание в одном из заголовочных файлов. Список заголовочных файлов определен
стандартом языка. Употребление директивы include не подключает соответствующую
стандартную библиотеку, а только позволяют вставить в текст программы описания из
указанного заголовочного файла. Подключение кодов библиотеки осуществляется на
этапе компоновки, т. е. после компиляции. Хотя в заголовочных файлах содержатся все
описания стандартных функций, в код программы включаются только те функции,
которые используются в программе.
После выполнения препроцессорной обработки в тексте программы не
остается ни одной препроцессорной директивы.
Программа представляет собой набор описаний и определений, и состоит из набора
функций. Среди этих функций всегда должна быть функция с именем main. Без нее
программа не может быть выполнена. Перед именем функции помещаются сведения о
типе возвращаемого функцией значения ( тип результата). Если функция ничего не
возвращает, то указывается тип void: void main ( ). Каждая функция, в том числе и main
должна иметь набор параметров, он может быть пустым, тогда в скобках указывается
(void).
За заголовком функции размещается тело функции. Тело функции - это
последовательность определений, описаний и исполняемых операторов, заключенных в
фигурные скобки. Каждое определение, описание или оператор заканчивается точкой с
запятой.
Определения - вводят объекты (объект - это именованная область памяти,
частный случай объекта - переменная), необходимые для представления в программе
обрабатываемых данных. Примером являются
int y = 10 ; //именованная константа
float x ; //переменная
Описания - уведомляют компилятор о свойствах и именах объектов и
функций, описанных в других частях программы.
Операторы
- определяют действия программы на каждом шаге ее
исполнения.
Пример программы на Си:
#include <stdio.h> //препроцессорная директива
void main()
//функция
{
//начало
printf(“Hello! “);
//печать
}
//конец
Контрольные вопросы
1.
2.
3.
4.
5.
6.
Из каких частей состоит программа на С++?
Чем определение отличается от объявления?
Перечислить этапы создания исполняемой программы на языке С++.
Что такое препроцессор?
Что такое директива препроцессора? Привести примеры директив препроцессора.
Составить программу, которая печатает текст «Моя первая программа на С++»
Лекция 4. Базовые средства языка СИ++
4.1 Состав языка
В тексте на любом естественном языке можно выделить четыре основных
элемента: символы, слова, словосочетания и предложения. Алгоритмический язык также
содержит такие элементы, только слова называют лексемами (элементарными
конструкциями ), словосочетания – выражениями, предложения – операторами. Лексемы
образуются из символов, выражения из лексем и символов, операторы из символов
выражений и лексем (Рис. 1.1)
Операторы
Выражения
Лексемы
Символы
Рис. 1.1. Состав алгоритмического языка
Таким образом, элементами алгоритмического языка являются:
1)
Алфавит языка СИ++, который включает
- прописные и строчные латинские буквы и знак подчеркивания;
- арабские цифры от 0 до 9;
- специальные знаки “{},| []()+-/%*.\’:;&?<>=!#^
- пробельные символы (пробел, символ табуляции, символы перехода на новую
строку).
2)
Из символов формируются лексемы языка:
- Идентификаторы – имена объектов СИ-программ. В идентификаторе могут быть
использованы латинские буквы, цифры и знак подчеркивания. Прописные и
строчные буквы различаются, например, PROG1, prog1 и Prog1 – три различных
идентификатора. Первым символом должна быть буква или знак подчеркивания
(но не цифра). Пробелы в идентификаторах не допускаются.
- Ключевые (зарезервированные) слова – это слова, которые имеют специальное
значение для компилятора. Их нельзя использовать в качестве идентификаторов.
- Знаки операций – это один или несколько символов, определяющих действие над
операндами. Операции делятся на унарные, бинарные и тернарную по количеству
участвующих в этой операции операндов.
- Константы – это неизменяемые величины. Существуют целые, вещественные,
символьные и строковые константы. Компилятор выделяет константу в качестве
лексемы (элементарной конструкции) и относит ее к одному из типов по ее
внешнему виду.
- Разделители – скобки, точка, запятая пробельные символы.
Константы в Си++
Константа – это лексема, представляющая изображение фиксированного
числового, строкового или символьного значения.
Константы делятся на 5 групп:
целые;
вещественные (с плавающей точкой);
перечислимые;
символьные;
строковые.
Компилятор выделяет лексему и относит ее к той или другой группе, а затем
внутри группы к определенному типу по ее форме записи в тексте программы и по
числовому значению.
Целые
константы
могут
быть
десятичными,
восьмеричными
и
шестнадцатеричными. Десятичная константа определяется как последовательность
десятичных цифр, начинающаяся не с 0, если это число не 0 (примеры: 8, 0, 192345).
Восьмеричная константа – это константа , которая всегда начинается с 0. За 0 следуют
восьмеричные цифры (примеры: 016 – десятичное значение 14, 01). Шестнадцатеричные
константы – последовательность шестнадцатеричных цифр, которым предшествуют
символы 0х или 0Х (примеры: 0хА, 0Х00F).
В зависимости от значения целой константы компилятор по-разному представит ее
в памяти компьютера (т. е. компилятор припишет константе соответствующий тип
данных).
Вещественные константы имеют другую форму внутреннего представления в
памяти компьютера. Компилятор распознает такие константы по их виду. Вещественные
константы могут иметь две формы представления: с фиксированной точкой и с
плавающей точкой. Вид константы с фиксированной точкой:[цифры].[цифры] (примеры:
5.7, .0001, 41.).Вид константы с плавающей точкой: [цифры][.][цифры]E|e[+|-][цифры]
(примеры:0.5е5, .11е-5, 5Е3). В записи вещественных констант может опускаться либо
целая, либо дробная части, либо десятичная точка, либо признак экспоненты с
показателем степени.
Перечислимые константы вводятся с помощью ключевого слова enum. Это
обычные целые константы, которым приписаны уникальны и удобные для использования
обозначения. Примеры: enum { one=1, two=2, three=3,four=4};
enum {zero,one,two,three} – если в определении перечислимых
констант опустить знаки = и числовые значения, то значения будут приписываться по
умолчанию. При этом самый левый идентификатор получит значение 0, а каждый
последующий будет увеличиваться на 1.
enum { ten=10, three=3, four, five, six};
enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday} ;
Символьные константы – это один или два символа, заключенные в апострофы.
Символьные константы, состоящие из одного символа, имеют тип char и занимают в
памяти один байт, символьные константы, состоящие из двух символов, имеют тип int и
занимают два байта. Последовательности, начинающиеся со знака \ , называются
управляющими, они используются:
- Для представления символов, не имеющих графического отображения, например:
\a – звуковой сигнал,
\b – возврат на один шаг,
\n – перевод строки,
\t – горизонтальная табуляция.
- Для представления символов: \ , ’ , ? , ” ( \\, \’ ,\? ,\” ).
- Для представления символов с помощью шестнадцатеричных или восьмеричных кодов
(\073, \0хF5).
Строковая константа – это последовательность символов, заключенная в кавычки.
Внутри строк также могут использоваться управляющие символы. Например: “\nНовая
строка”,
-
“\n\”Алгоритмические языки программирования высокого уровня \”” .
4.2. Типы данных в Си++
Данные отображают в программе окружающий мир. Цель программы состоит в
обработке данных. Данные различных типов хранятся и обрабатываются по-разному. Тип
данных определяет:
1) внутреннее представление данных в памяти компьютера;
2) множество значений, которые могут принимать величины этого типа;
3) операции и функции, которые можно применять к данным этого типа.
В зависимости от требований задания программист выбирает тип для объектов
программы. Типы Си++ можно разделить на простые и составные. К простым типам
относят типы, которые характеризуются одним значением. В Си++ определено 6 простых
типов данных:
int (целый)
char (символьный)
целочисленные
wchar_t (расширенный символьный)
bool (логический)
с плавающей точкой
float(вещественный)
(число=мантисса х 10к )
double (вещественный с двойной точностью)
Существует 4 спецификатора типа, уточняющих внутреннее представление и
диапазон стандартных типов
short (короткий)
long (длинный)
signed (знаковый)
unsigned (беззнаковый)
Тип int
Значениями этого типа являются целые числа.
Размер типа int не определяется стандартом, а зависит от компьютера и
компилятора. Для 16-разрядного процессора под него отводится 2 байта, для 32разрядного – 4 байта.
Если перед int стоит спецификатор short, то под число отводится 2 байта, а если
спецификатор long, то 4 байта. От количества отводимой под объект памяти зависит
множество допустимых значений, которые может принимать объект:
short int - занимает 2 байта, следовательно, имеет диапазон –32768 ..+32767;
long int – занимает 4 байта, следовательно, имеет диапазон –2 147 483 648..+2 147
483 647
Тип int совпадает с типом short int на 16-разрядных ПК и с типом long int на 32разрядных ПК.
Модификаторы signed и unsigned также влияют на множество допустимых
значений, которые может принимать объект:
unsigned short int - занимает 2 байта, следовательно, имеет диапазон 0 ..65536;
unsigned long int – занимает 4 байта, следовательно, имеет диапазон 0..+4 294 967
295.
Тип char
Значениями этого типа являются элементы конечного упорядоченного множества
символов. Каждому символу ставится в соответствие число, которое называется кодом
символа. Под величину символьного типа отводится 1 байт. Тип char может
использоваться со спецификаторами signed и unsigned. В данных типа signed char можно
хранить значения в диапазоне от –128 до 127. При использовании типа unsigned char
значения могут находиться в диапазоне от 0 до 255. Для кодировки используется код
ASCII(American Standard Code foe International Interchange). Символы с кодами от 0 до 31
относятся к служебным и имеют самостоятельное значение только в операторах вводавывода.
Величины типа char также применяются для хранения чисел из указанных
диапазонов.
Тип wchar_t
Предназначен для работы с набором символов, для кодировки которых
недостаточно 1 байта, например Unicode. Размер этого типа, как правило, соответствует
типу short. Строковые константы такого типа записываются с префиксом L: L“String #1”.
Тип bool
Тип bool называется логическим. Его величины могут принимать значения true и
false. Внутренняя форма представления false – 0, любое другое значение интерпретируется
как true.
Типы с плавающей точкой.
Внутреннее представление вещественного числа состоит из 2 частей: мантиссы и
порядка. В IBM-совместимых ПК величины типа float занимают 4 байта, из которых один
разряд отводится под знак мантиссы, 8 разрядов под порядок и 24 – под мантиссу.
Величины типы double занимают 8 байтов, под порядок и мантиссу отводятся 11 и
52 разряда соответственно. Длина мантиссы определяет точность числа, а длина порядка
его диапазон.
Если перед именем типа double стоит спецификатор long, то под величину
отводится байтов.
Тип void
К основным типам также относится тип void Множество значений этого типа –
пусто.
Переменные
Переменная в СИ++ - именованная область памяти, в которой хранятся данные
определенного типа. У переменной есть имя и значение. Имя служит для обращения к
области памяти, в которой хранится значение. Перед использованием любая переменная
должна быть описана. Примеры:
int a; float x;
Общий вид оператора описания:
[класс памяти][const]тип имя [инициализатор];
Класс памяти может принимать значения: auto, extern, static, register. Класс памяти
определяет время жизни и область видимости переменной. Если класс памяти не указан
явно, то компилятор определяет его исходя из контекста объявления. Время жизни может
быть постоянным – в течение выполнения программы или временным – в течение блока.
Область видимости – часть текста программы, из которой допустим обычный доступ к
переменной. Обычно область видимости совпадает с областью действия. Кроме того
случая, когда во внутреннем блоке существует переменная с таким же именем.
Const – показывает, что эту переменную нельзя изменять (именованная константа).
При описании можно присвоить переменной начальное значение (инициализация).
Классы памяти:
auto –автоматическая локальная переменная. Спецификатор auto может быть задан
только при определении объектов блока, например, в теле функции. Этим переменным
память выделяется при входе в блок и освобождается при выходе из него. Вне блока такие
переменные не существуют.
extern – глобальная переменная, она находится в другом месте программы (в
другом файле или долее по тексту). Используется для создания переменных, которые
доступны во всех файлах программы.
static – статическая переменная, она существует только в пределах того файла, где
определена переменная.
register - аналогичны auto, но память под них выделяется в регистрах процессора.
Если такой возможности нет, то переменные обрабатываются как auto.
Пример
int a; //глобальная переменная
void main(){
int b;//локальная переменная
extern int x;//переменная х определена в другом месте
static int c;//локальная статическая переменная
a=1;//присваивание глобальной переменной
int a;//локальная переменная а
a=2;//присваивание локальной переменной
::a=3;//присваивание глобальной переменной
}
int x=4;//определение и инициализация х
В примере переменная а определена вне всех блоков. Областью действия
переменной а является вся программа, кроме тех строк, где используется локальная
переменная а. Переменные b и с – локальные, область их видимости – блок. Время жизни
различно: память под b выделяется при входе в блок (т. к. по умолчанию класс памяти
auto), освобождается при выходе из него. Переменная с (static) существует, пока работает
программа.
Если при определении начальное значение переменным не задается явным образом,
то компилятор обнуляет глобальные и статические переменные. Автоматические
переменные не инициализируются..
Имя переменной должно быть уникальным в своей области действия.
Описание переменной может быть выполнено или как объявление, или как
определение. Объявление содержит информацию о классе памяти и типе переменной,
определение вместе с этой информацией дает указание выделить память. В примере extern
int x; - объявление, а остальные – определения.
4.2 Знаки операций в Си++
Знаки операций обеспечивают формирование выражений. Выражения состоят из
операндов, знаков операций и скобок. Каждый операнд является, в свою очередь,
выражением или частным случаем выражения – константой или переменной.
Унарные операции
&
получение адреса операнда
*
Обращение по адресу (разыменование)
унарный минус, меняет знак арифметического операнда
~
поразрядное инвертирование внутреннего двоичного кода
целочисленного операнда (побитовое отрицание)
логическое отрицание (НЕ). В качестве логических значений
используется 0 - ложь и не 0 - истина, отрицанием 0 будет 1,
отрицанием любого ненулевого числа будет 0.
++
Увеличение на единицу:
префиксная операция - увеличивает операнд до его
использования,
постфиксная операция увеличивает операнд после его
использования.
int m=1,n=2;
int a=(m++)+n; // a=4,m=2,n=2
int b=m+(++n);//a=3,m=1,n=3
-уменьшение на единицу:
префиксная операция - уменьшает операнд до его
использования,
постфиксная операция уменьшает операнд после его
использования.
sizeof
вычисление размера (в байтах) для объекта того типа, который
имеет операнд
имеет две формы
sizeof выражение
sizeof (тип)
Примеры:
sizeof(float)//4
sizeof(1.0)//8, т. к. вещественные константы по умолчанию
имеют тип double
Бинарные операции.
Аддитивные:
+
бинарный плюс (сложение арифметических операндов)
бинарный минус (вычитание арифметических операндов)
Мультипликативные:
*
умножение операндов арифметического типа
/
деление операндов арифметического типа (если операнды
целочисленные, то выполняется целочисленное деление)
%
получение остатка от деления целочисленных операндов
Операции сдвига (определены только для целочисленных операндов).
Формат выражения с операцией сдвига:
операнд_левый операция_сдвига операнд_правый
<
сдвиг
влево
битового
представления
значения
левого
целочисленного операнда на количество разрядов, равное значению
правого операнда, освободившиеся разряды обнуляются
>
сдвиг вправо битового представления значения правого
целочисленного операнда на количество разрядов, равное значению
правого операнда, освободившиеся разряды обнуляются, если операнд
беззнакового типа и заполняются знаковым разрядом, если – знакового
Поразрядные операции:
&
поразрядная конъюнкция (И) битовых представлений значений
целочисленных операндов (бит =1, если соответствующие биты обоих
операндов=1)
|
поразрядная дизъюнкция (ИЛИ) битовых представлений значений
целочисленных операндов (бит =1, если соответствующий бит одного из
!
<
>
операндов=1)
^
поразрядное исключающее ИЛИ битовых представлений значений
целочисленных операндов(бит =1, если соответствующий бит только
одного из операндов=1)
Операции сравнения: результатом являются true( не 0) или false(0)
<
меньше, чем
>
больше, чем
<
меньше или равно
=
>
больше или равно
=
Равно
!
не равно
=
=
=
Логические бинарные операции:
&
конъюнкция (И) целочисленных операндов или отношений,
&
целочисленный результат ложь(0) или истина(не 0)
|
дизъюнкция (ИЛИ) целочисленных операндов или отношений,
|
целочисленный результат ложь(0) или истина(не 0)
Операции присваивания
=, +=, -=, += и т.д.
Формат операции простого присваивания:
операнд1=операнд2
Леводопустимое значение (L-значение) – выражение, которое адресует некоторый
участок памяти, т. е. в него можно занести значение. Это название произошло от операции
присваивания, т. к. именно левая часть операции присваивания определяет, в какую
область памяти будет занесен результат операции. Переменная – это частный случай
леводопустимого выражения.
Условная операция.
В отличие от унарных и бинарных операций в ней используется три операнда.
Выражение1 ? Выражение2 : Выражение3;
Первым вычисляется значение выражения1. Если оно истинно, то вычисляется
значение выражения2, которое становится результатом. Если при вычислении
выражения1 получится 0, то в качестве результата берется значение выражения3.
Например:
x<0 ? -x : x ; //вычисляется абсолютное значение x.
Операция явного (преобразования) приведения типа.
Существует две формы: каноническая и функциональная:
1) (имя_типа) операнд
2) имя_типа (операнд)
(int)a //каноническая форма
int(a) //функциональная форма
4.3 Выражения
Из констант, переменных, разделителей и знаков операций можно конструировать
выражения. Каждое выражение представляет собой правило вычисления нового значения..
Если выражение формирует целое или вещественное число, то оно называется
арифметическим. Пара арифметических выражений, объединенная операцией сравнения,
называется отношением. Если отношение имеет ненулевое значение, то оно – истинно,
иначе – ложно.
Приоритеты операций в выражениях
Ранг
1
2
3
5
6
7
8
9
10
11
12
13
14
15
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
Операции
( ) [ ] -> .
! ~ - ++ -- & * (тип) sizeof тип( )
* / % (мультипликативные бинарные)
+ - (аддитивные бинарные)
<< >> (поразрядного сдвига)
< > <= >= (отношения)
== != (отношения)
& (поразрядная конъюнкция «И»)
^ (поразрядное исключающее «ИЛИ»)
| (поразрядная дизъюнкция «ИЛИ»)
&& (конъюнкция «И»)
|| (дизъюнкция «ИЛИ»)
?: (условная операция)
= *= /= %= -= &= ^= |= <<=
(операция присваивания)
, (операция запятая)
>>=
Контрольные вопросы
Из каких элементов состоит естественный язык? Что является аналогами этих
элементов в С++?
Что такое лексема? Привести примеры лексем в языке С++.
Что такое идентификатор? Правила записи идентификаторов.
Что такое константа? Как константа обрабатывается компилятором?
Какие типы констант существуют в С++. Привести примеры констант разных типов.
К какому типу относятся константы 192345, 0х56, 0хСВ, 016, 0.7865, .0045, ‘c’, “x”,
one, “one”, 5 , 5.?
Что такое тип данных?
Чем отличаются типы данных: float и double, char и wchar_t, int и short int?
Чем отличаются типы данных int и unsigned int?
Перечислить все типы данных, которые существуют в C++. Сколько места в памяти
занимают данные каждого типа?
На что влияет количество памяти, выделяемое для данных определенного типа?
Что такое переменная? Чем объявление переменой отличается от ее определения?
Привести примеры определений и объявлений.
Какие операции присваивания существуют в С++?
Привести примеры выражений, содержащих операции присваивания, операции
инкремента (декремента), аддитивные и мультипликативные операции. Пояснить, как
они будут выполняться.
Лекция 5. Ввод и вывод данных
В языке Си++ нет встроенных средств ввода и вывода – он осуществляется с
помощью функций, типов и объектов, которые находятся в стандартных библиотеках.
Существует два основных способа: функции унаследованные из Си и объекты Си++.
Для ввода/вывода данных в стиле Си используются функции, которые
описываются в библиотечном файле stdio.h.
1)
printf ( форматная строка, список аргументов);
форматная строка - строка символов, заключенных в кавычки, которая показывает,
как должны быть напечатаны аргументы. Например:
printf ( “Значение числа Пи равно %f\n”, pi);
Форматная строка может содержать
1)
символы печатаемые текстуально;
2)
спецификации преобразования;
3)
управляющие символы.
Каждому аргументу соответствует своя спецификация преобразования:
%d, %i - десятичное целое число;
%f - число с плавающей точкой;
%e,%E – число с плавающей точкой в экспоненциальной форме;
%u – десятичное число в беззнаковой форме;
%c - символ;
%s - строка.
В форматную строку также могут входить управляющие символы:
\n - управляющий символ новая строка;
\t – табуляция;
\a – звуковой сигнал и др.
Также в форматной строке могут использоваться модификаторы формата, которые
управляют шириной поля, отводимого для размещения выводимого значения.
Модификаторы – это числа, которые указывают минимальное количество позиций для
вывода значения и количество позиций ля вывода дробной части числа:
%[-]m[.p]C, где
1.
- задает выравнивание по левому краю,
m – минимальная ширина поля,
p – количество цифр после запятой для чисел с плавающей точкой и
минимальное количество выводимых цифр для целых чисел (если цифр в числе
меньше, чем значение р, то выводятся начальные нули),
С- спецификация формата вывода.
Пример
printf("\nСпецификации формата:\n%10.5d - целое,\n%10.5f - с плавающей точкой\
\n%10.5e – в экспоненциальной форме\n%10s - строка",10,10.0,10.0,"10");
Будет выведено:
Спецификации формата:
00010 – целое
10.00000 – с плавающей точкой
1.00000е+001 - в экспоненциальной форме
10 – строка.
2) scanf ( форматная строка, список аргументов);
В качестве аргументов используются адреса переменных. Например:
scanf(“ %d%f ”, &x,&y);
При использовании библиотеки классов Си++, Используется библиотечный файл
iostream.h, в котором определены стандартные потоки ввода данных от клавиатуры cin и
вывода данных на экран дисплея cout, а также соответствующие операции
<< - операция записи данных в поток;
>> - операция чтения данных из потока.
Например:
#include <iostream.h>;
.........
cout << “\nВведите количество элементов: ”;
cin >> n;
1)
2)
Контрольные вопросы
1. Что такое форматная строка? Что содержит форматная строка функции printf? Что
содержит форматная строка функции scanf?
2. Что такое спецификация преобразования? Привести примеры спецификаций
преобразования для различных типов данных.
3. Что будет выведено функцией
printf("\nСреднее арифметическое последовательности чисел равно: %10.5f
\nКоличество четных элементов последовательности равно%10.5d ",S/n,k);
4. Как записать вывод результатов из вопроса 3 с помощью операции << ?
5. Как выполнить ввод переменных х и у, где x типа long int, а у типа double с
помощью функции scanf? С помощью операции >> ?
Лекция 6. Основные операторы языка Си++»
6.1. Базовые конструкции структурного программирования
В теории программирования доказано, что программу для решения задачи любой
сложности можно составить только из трех структур: линейной, разветвляющейся и
циклической. Эти структуры называются базовыми конструкциями структурного
программирования.
Линейной называется конструкция, представляющая собой последовательное
соединение двух или более операторов.
Ветвление – задает выполнение одного из двух операторов, в зависимости от
выполнения какого либо условия.
Цикл – задает многократное выполнение оператора.
Следование
Ветвление
Цикл
Целью использования базовых конструкций является получение программы
простой структуры. Такую программу легко читать, отлаживать и при необходимости
вносить в нее изменения. Структурное программирование также называют
программированием без goto, т. к. частое использование операторов перехода затрудняет
понимание логики работы программы. Но иногда встречаются ситуации, в которых
применение операторов перехода, наоборот, упрощает структуру программы.
Операторы управления работой программы называют управляющими
конструкциями программы. К ним относят:
 составные операторы;
 операторы выбора;
 операторы циклов;
 операторы перехода.
6.2. Оператор «выражение»
Любое выражение, заканчивающееся точкой с запятой, рассматривается как
оператор, выполнение которого заключается в вычислении этого выражения. Частным
случаем выражения является пустой оператор ;.
Примеры:
i++;
a+=2;
x=a+b;
6.3. Составные операторы
К составным операторам относят собственно составные операторы и блоки. В
обоих случаях это последовательность операторов, заключенная в фигурные скобки. Блок
отличается от составного оператора наличием определений в теле блока. Например:
{
n++;
это составной оператор
summa+=n;
}
{
int n=0;
n++;
summa+=n;
}
это блок
6.4. Операторы выбора
Операторы выбора - это условный оператор и переключатель.
1. Условный оператор имеет полную и сокращенную форму.
if (выражение-условие ) оператор;
//сокращенная форма
В качестве выражения-условия могут использоваться арифметическое выражение,
отношение и логическое выражение. Если значение выражения-условия отлично от нуля
(т. е. истинно), то выполняется оператор. Например:
if (x<y&&x<z)min=x;
if ( выражение-условие ) оператор1;
//полная форма
else оператор2;
Если значение выражения-условия отлично от нуля, то выполняется оператор1, при
нулевом значении выражения-условия выполняется оператор2.Например:
if (d>=0)
{
x1=(-b-sqrt(d))/(2*a);
x2=(-b+sqrt(d))/(2*a);
cout<< “\nx1=”<<x1<<“x2=”<<x2;
}
else cout<<“\nРешения нет”;
2.Переключатель определяет множественный выбор.
switch (выражение)
{
case константа1 : оператор1 ;
case константа2 : оператор2 ;
...........
[default: операторы;]
}
При выполнении оператора switch, вычисляется выражение, записанное после
switch, оно должно быть целочисленным. Полученное значение последовательно
сравнивается с константами, которые записаны следом за case. При первом же совпадении
выполняются операторы помеченные данной меткой. Если выполненные операторы не
содержат оператора перехода, то далее выполняются операторы всех следующих
вариантов, пока не появится оператор перехода или не закончится переключатель. Если
значение выражения, записанного после switch не совпало ни с одной константой, то
выполняются операторы, которые следуют за меткой default. Метка default может
отсутствовать.
Пример:
#include <iostream.h>
void main()
{
int i;
cout<<"\nEnter the number";
cin>>i;
switch(i)
{
case 1:cout<<"\nthe number is one";
case 2:cout<<"\n2*2="<<i*i;
case 3: cout<<"\n3*3="<<i*i;break;
case 4: cout<<"\n"<<i<<" is very beautiful!";
default:cout<<"\nThe end of work";
}
}
Результаты работы программы:
1. При вводе 1 будет выведено:
The number is one
2*2=1
3*3=1
2. При вводе 2 будет выведено:
2*2=4
3*3=4
3. При вводе 3 будет выведено:
3*3=9
4. При вводе 4 будет выведено:
4 is very beautiful!
5. При вводе всех остальных чисел будет выведено:
The end of work
6.5. Операторы циклов
Различают:
1)
итерационные циклы;
2)
арифметические циклы.
Группа действий, повторяющихся в цикле, называется его телом. Однократное
выполнение цикла называется его шагом.
В итерационных циклах известно условие выполнения цикла.
1. Цикл с предусловием:
while (выражение-условие)
оператор;
В качестве <выражения-условия> чаще всего используется отношение или
логическое выражение. Если оно истинно, т. е. не равно 0, то тело цикла выполняется до
тех пор, пока выражение-условие не станет ложным.
Пример
while (a!=0)
{
cin>>a;
s+=a;
}
2. Цикл с постусловием:
do
оператор
while (выражение-условие);
Тело цикла выполняется до тех пор, пока выражение-условие истинно.
Пример:
do
{
cin>>a;
s+=a;
}
while(a!=0);
3. Цикл с параметром:
for ( выражение_1;выражение-условие;выражение_3)
оператор;
выражение_1 и выражение_3 могут состоять из нескольких выражений,
разделенных запятыми. Выражение_1 - задает начальные условия для цикла
(инициализация). Выражение-условие> определяет условие выполнения цикла, если оно
не равно 0, цикл выполняется, а затем вычисляется значение выражения_3. Выражение_3
- задает изменение параметра цикла или других переменных (коррекция). Цикл
продолжается до тех пор, пока выражение-условие не станет равно 0. Любое выражение
может отсутствовать, но разделяющие их « ; » должны быть обязательно.
Примеры использования цикла с параметром.
1) Уменьшение параметра:
for ( n=10; n>0; n--)
{ оператор};
2) Изменение шага корректировки:
for ( n=2; n>60; n+=13)
{ оператор };
3) Возможность проверять условие отличное от условия, которое налагается на
число итераций:
for ( num=1;num*num*num<216; num++)
{ оператор };
4) Коррекция может осуществляться не только с помощью сложения или
вычитания:
for ( d=100.0; d<150.0;d*=1.1)
{ <тело цикла>};
for (x=1;y<=75;y=5*(x++)+10)
{ оператор };
5) Можно использовать несколько инициализирующих или корректирующих
выражений:
for (x=1, y=0; x<10;x++;y+=x);
6.6 Операторы перехода
Операторы перехода выполняют безусловную передачу управления.
1) break - оператор прерывания цикла.
{
< операторы>
if (<выражение_условие>) break;
<операторы>
}
Т. е. оператор break целесообразно использовать, когда условие продолжения
итераций надо проверять в середине цикла.
Пример:
// ищет сумму чисел вводимых с клавиатуры до тех пор, пока не будет введено 100
чисел или 0
for(s=0, i=1; i<100;i++)
{
cin>>x;
if( x==0) break; // если ввели 0, то суммирование заканчивается
s+=x;
}
2) continue - переход к следующей итерации цикла. Он используется, когда тело
цикла содержит ветвления.
Пример:
//ищет количество и сумму положительных чисел
for( k=0,s=0,x=1;x!=0;)
{
cin>>x;
if (x<=0) continue;
k++;s+=x;
}
3) Оператор goto
Оператор goto имеет формат: goto метка;
В теле той же функции должна присутствовать конструкция: метка:оператор;
Метка – это обычный идентификатор, областью видимости которого является
функция. Оператор goto передает управления оператору, стоящему после метки.
Использование оператора goto оправдано, если необходимо выполнить переход из
нескольких вложенных циклов или переключателей вниз по тексту программы или
перейти в одно место функции после выполнения различных действий.
Применение
goto
нарушает
принципы
структурного
и
модульного
программирования, по которым все блоки, из которых состоит программа, должны иметь
только один вход и только один выход.
Нельзя передавать управление внутрь операторов if, switch и циклов. Нельзя
переходить внутрь блоков, содержащих инициализацию, на операторы, которые стоят
после инициализации. Пример:
int k;
goto m;
...
{
int a=3,b=4;
k=a+b;
m: int c=k+1;
...
}
В этом примере при переходе на метку m не будет выполняться инициализация
переменных a , b и k.
4) Оператор return – оператор возврата из функции. Он всегда завершает
выполнение функции и передает управление в точку ее вызова. Вид оператора:
return [выражение].
Лекция7. Примеры решения задач с использованием основных операторов
С++
«Начинающие программисты, особенно студенты, часто пишут программы так:
получив задание, тут же садятся за компьютер и начинают кодировать те фрагменты
алгоритма, которые им удается придумать сразу. Переменным дают первые попавшиеся
имена типа х и у. Когда компьютер зависает, делается перерыв, после которого все
написанное стирается, и все повторяется заново. Периодически высказываются сомнения
в правильности работы компилятора, компьютера и операционной системы. Когда
программа доходит до стадии выполнения, в нее вводятся произвольные значения, после
чего экран становится объектом пристального удивленного изучения. «Работает» такая
программа обычно только в бережных руках хозяина на одном наборе данных, а внесение
в нее изменений может привести автора к потере веры в себя и ненависти к процессу
программирования.
Ваша задача состоит в том, чтобы научиться подходить к программированию
профессионально. В конце концов, профессионал отличается тем, что может достаточно
точно оценить, сколько времени у него займет написание программы, которая будет
работать в полном соответствии с поставленной задачей. Кроме «ума, вкуса и терпения»,
для этого требуется опыт, а также знание основных принципов, выработанных
программистами в течение более, чем полувека развития этой дисциплины. Даже к
написанию самых простых программ нужно подходить последовательно, соблюдая
определенную дисциплину.» (Павловская Т. А., стр.109)
Решение задач по программированию предполагает ряд этапов:
1)
Разработка математической модели. На этом этапе определяются исходные данные
и результаты решения задачи, а также математические формулы, с помощью которых
можно перейти от исходных данных к конечному результату.
2)
Разработка алгоритма. Определяются действия, выполняя которые можно будет от
исходных данных придти к требуемому результату.
3)
Запись программы на некотором языке программирования. На этом этапе каждому
шагу алгоритма ставится в соответствие конструкция выбранного алгоритмического
языка.
4)
Выполнение программы (исходный модуль ->компилятор ->объектный модуль ->
компоновщик -> исполняемый модуль)
5)
Тестирование и отладка программы.
При выполнении программы могут
возникнуть ошибки 3 типов:
a. синтаксические – исправляются на этапе компиляции;
b. ошибки исполнения программы (деление на 0, логарифм от отрицательного числа и
т. п.) – исправляются при выполнении программы;
c. семантические (логические) ошибки – появляются из-за неправильно понятой
задачи, неправильно составленного алгоритма.
Чтобы устранить эти ошибки программа должна быть выполнена на некотором
наборе тестов. Цель процесса тестирования – определение наличия ошибки, нахождение
места ошибки, ее причины и соответствующие изменения программы – исправление. Тест
– это набор исходных данных, для которых заранее известен результат. Тест выявивший
ошибку считается успешным. Отладка программы заканчивается, когда достаточное
количество тестов выполнилось неуспешно, т. е. программа на них выдала правильные
результаты.
Для определения достаточного количества тестов существует два подхода. При
первом подходе программа рассматривается как «черный ящик», в который передают
исходные данные и получают результаты. Устройство самого ящика неизвестно. При этом
подходе, чтобы осуществить полное тестирование, надо проверить программу на всех
входных данных, что практически невозможно. Поэтому вводят специальные критерии,
которые должны показать, какое конечное множество тестов является достаточным для
программы. При первом подходе чаще всего используются следующие критерии:
1)
тестирование классов входных данных, т. е. набор тестов должен содержать
по одному представителю каждого класса данных:
X 0 1 0 1 - 1 1
1
Y 0 1 1 0 1 - 1 1
2)
тестирование классов выходных данных, набор тестов должен содержать
данные достаточные для получения по одному представителю из каждого класса
выходных данных.
При втором подходе программа рассматривается как «белый ящик», для которого
полностью известно устройство. Полное тестирование при этом подходе заканчивается
после проверки всех путей, ведущих от начала программы к ее концу. Однако и при таком
подходе полное тестирование программы невозможно, т. к. путей в программе с циклами
бесконечное множество. При таком подходе используются следующие критерии:
1)
Тестирование команд. Набор тестов должен обеспечивать прохождение
каждой команды не менее одного раза.
2)
Тестирование ветвей. Набор тестов в совокупности должен обеспечивать
прохождение каждой ветви не менее одного раза. Это самый распространенный критерий
в практике программирования.
7.1. Программирование ветвлений
Задача №1. Определить, попадет ли точка с координатами (х, у ) в заштрихованную
область.
3
1
-3
-1
1
2
-1
-3
Исходные данные: х,у
Результат: да или нет
Математическая модель:
Ok=I || II || III || VI, где I, II, III, IV – условия попадания точки в заштрихованную
область для каждого квадранта.
Квадрант I: Область формируется прямыми 0Х и 0У, прямой, проходящей через
точки (0,1)и(1,0) и прямой, проходящей через точки (0,3) и (2,0).
Необходимо определить уравнения прямых у=ax+b. Решаем две системы
уравнений:
1)
1=a*0+b;
0=a*1+b;
2)
2=a*0+b;
0=a*3+b;
Из этих систем получаем следующие уравнения прямых:
y=-1x+1;
y=-2/3x+1;
Тогда условие попадания точки в I квадрант будет выглядеть следующим образом:
y>=-x+1&&y<=-2/3x+2&&y>=0&&x>=0.
Квадранты II и III: Область формируется прямыми 0Х и 0У и двумя окружностями,
описываемыми формулами x2+y2=1, x2+y2=9.
Тогда условие попадания точки во II и III квадранты будет выглядеть следующим
образом:
x2+y2>=1&& x2+y2<=9&&&&x<=0.
Квадрант IV:
Область формируется двумя прямоугольниками. Точка может попадать либо в
первый прямоугольник, либо во второй.
Условие попадания точки в IV квадрант будет выглядеть следующим образом:
(x>=0&&x<=1&&y<=-1&&y>=-3)|| (x>=1&&x<=3&&y<=0&&y>=-3) .
Программа:
#include <iostream.h>
#include <math.h>
void main()
{
float x,y;
cout<<"\nEnter x,y";
cin>>x>>y;
bool Ok=(y>=-x+1&&y<=2/3*x+2&&x>=0&&y>=0)||
(pow(x,2)+pow(y,2)>=1&&pow(x,2)+pow(y,2)<=9&&x<=0)||
(x>=0&&x<=1&&y<=-1&&y>=-3)||(x>=1&&x<=2&&y<=0&&y>=-3);
cout<<"\n"<<Ok;
}
Тесты:
Квадрант
Исходные
данные
Результат (Ok)
(X,Y)
I
0.2,0.2
0
I
0.7,0.5
1
II
-0.5, 0.5
0
II
-2,0
1
III
-0.5,-0,5
0
III
-2,-1
1
IV
0,5,-0.5
0
IV
1.5, -1
1
Центр
системы
0,0
0
координат
7.2. Программирование арифметических циклов.
Для арифметического цикла заранее известно сколько раз выполняется тело цикла.
Задача №2
Дана последовательность целых чисел из
арифметическое этой последовательности.
#include <iostream.h>
#include <math.h>
void main()
{
int a,n,i,k=0;
double s=0;
cout<<"\nEnter n";
cin>>n;
for(i=1;i<=n;i++)
{
cout<<"\nEnter a";
cin>>a;
s+=a;k++;
}
s=s/k;
cout<<"\nSr. arifm="<<s<<"\n";
}
Тесты
N 5
A 1,2
,3,4,5,3
S 3
=-1
=0
=5
Задача №3
S=1+2+3+4+. . . +N
#include <iostream.h>
#include <math.h>
void main()
{
int n,i,s=0;
cout<<"\nEnter n";
cin>>n;
if(n<=0) {cout<<”\nN<=0”;return;}
for(i=1;i<=n;i++)s+=i;
cout<<"\nS="<<s<<"\n";
}
Тесты
n
S
n
N
<=0
n
N
<=0
n
S
=15
Задача №4
S=15-17+19-21+ . . ., всего n слагаемых.
#include <iostream.h>
#include <math.h>
n
элементов.
Найти
среднее
=-1
=0
=3
void main()
{
int n,i,s=0,a=15;
cout<<"\nEnter n";
cin>>n;
if(n<=0) {cout<<”\nN<=0”;return;}
for(i=1;i<=n;i++)
{
if(i%2==1)s+=a;
else s-=a;
a+=2;
}
cout<<"\nS="<<s<<"\n";
}
Тесты
n
S
n
N
<=0
n
N
<=0
n
S
=17
7.3. Итерационные циклы
Для итерационного цикла известно условие выполнения цикла.
Задача №5
Дана последовательность целых чисел, за которой следует 0. Найти минимальный
элемент этой последовательности.
#include <iostream.h>
#include <math.h>
void main()
{
int a,min;
cout<<"\nEnter a";
cin>>a;
min=a;
while(a!=0)//for(;a!=0;)
{
cout<<"\nEnter a";
cin>>a;
if (a!=0&&a<min)min=a;
}
cout<<"\nmin="<<min<<"\n";
}
Тесты:
a
2
5 - 0
5 3 10
m
in
10
a
1
5
4 2
0
2 5
m
4
7
a
6
43
m
10
15
in
in
1
-
0
Задача №6 : Найти сумму чисел Фибоначчи, меньших заданного числа Q.
#include<iostream.h>
void main()
{
int a=1,b=1,s=2,Q,c;
cout<<"\nEnter Q";
cin>>Q;
if(Q<=0)cout<<"Error in Q";
else
if(Q==1)cout<<"\nS=1";
else
{
c=a+b;
while(c<Q) //for(;c!=0;)
{
s+=c;
a=b;
b=c;
c=a+b;
}
cout<<"\nS="<<s<<"\n";
}
}
Тесты:
Q S
Er
ror in Q
0 Er
ror in Q
1 1
2 2
1 20
0
1
10
Тесты:
Q
Er
ror in Q
0 Er
ror in Q
1 2
2 2
3
0
1 2
3 5 7 11
7.4. Вложенные циклы
Задача №7: Напечатать N простых чисел.
#include<iostream.h>
void main()
{
int a=1,n,d;
cout<<"\nEnter N";
cin>>n;
for(int i=0;i<n;)//внешний цикл
{
a++;d=1;
do //внутренний цикл
{
d++;
}
while(a%d!=0);//конец внутреннего цикла
if(a==d){
cout<<a<<" ";
i++;}
}//конец внешнего цикла
}
Лекция 8. Массивы
В языке Си/Си++ ,кроме базовых типов, разрешено вводить и использовать
производные типы, полученные на основе базовых. Стандарт языка определяет три
способа получения производных типов:
массив элементов заданного типа;
указатель на объект заданного типа;
функция, возвращающая значение заданного типа.
Массив – это упорядоченная последовательность переменных одного типа.
Каждому элементу массива отводится одна ячейка памяти. Элементы одного массива
занимают последовательно расположенные ячейки памяти. Все элементы имеют одно имя
- имя массива и отличаются индексами – порядковыми номерами в массиве. Количество
элементов в массиве называется его размером. Чтобы отвести в памяти нужное
количество ячеек для размещения массива, надо заранее знать его размер. Резервирование
памяти для массива выполняется на этапе компиляции программы.
8.1. Определение массива в Си/Си++
int a[100];//массив из 100 элементов целого типа
Операция sizeof(a) даст результат 400, т. е.100 элементов по 4 байта.
Элементы массива всегда нумеруются с 0.
0
1
2
..
…
9
9
Чтобы обратиться к элементу массива, надо указать имя массива и номер элемента
в массиве (индекс):
a[0] – индекс задается как константа,
a[55] – индекс задается как константа,
a[I] – индекс задается как переменная,
a[2*I] – индекс задается как выражение.
Элементы массива можно задавать при его определении:
int a[10]={1,2,3,4,5,6,7,8,9,10} ;
Операция sizeof(a) даст результат 40, т. е.10 элементов по 4 байта.
int a[10]={1,2,3,4,5};
Операция sizeof(a) даст результат 40, т. е.10 элементов по 4 байта. Если количество
начальных значений меньше, чем объявленная длина массива, то начальные элементы
массива получат только первые элементы.
int a[]={1,2,3,4,5};
Операция sizeof(a) даст результат 20, т. е.5 элементов по 4 байта. Длин массива
вычисляется компилятором по количеству значений, перечисленных при инициализации.
8.2. Обработка одномерных массивов
-
При работе с массивами очень часто требуется одинаково обработать все элементы
или часть элементов массива. Для этого организуется перебор массива.
Перебор элементов массива характеризуется:
направлением перебора;
количеством одновременно обрабатываемых элементов;
характером изменения индексов.
По направлению перебора массивы обрабатывают :
слева направо (от начала массива к его концу);
справа налево (от конца массива к началу);
от обоих концов к середине.
Индексы могут меняться
линейно (с постоянным шагом);
нелинейно (с переменным шагом).
Перебор массива по одному элементу
Элементы можно перебирать:
1)
Слева направо с шагом 1, используя цикл с параметром
for(int I=0;I<n;I++){обработка a[I];}
2)
Слева направо с шагом отличным от 1, используя цикл с параметром
for (int I=0;I<n;I+=step){обработка a[I];}
3)
Справа налево с шагом 1, используя цикл с параметром
for(int I=n-1;I>=0;I--){обработка a[I];}
4)
Справа налево с шагом отличным от 1, используя цикл с параметром
for (int I=n-1;I>=0;I-=step){обработка a[I];}
Использование датчика случайных чисел для формирования
массива.
Датчик случайных чисел (ДСЧ) – это программа, которая формирует
псевдослучайное число. Простейший ДСЧ работает следующим образом:
1) Берется большое число К и произвольное x0  [0,1] .
2) Формируются числа х1=дробная_часть(х0*К); х2=дробная_часть(х1*К); и т. д.
В результате получается последовательность чисел х0, х1, х2,. . . беспорядочно
разбросанных по отрезку от 0 до 1. Их можно считать случайными, а точнее
псевдослучайными. Реальные ДСЧ реализуют более сложную функцию f(x).
В Си++ есть функция
int rand() – возвращает псевдослучайное число из диапазона 0..RAND_MAX=32767,
описание функции находится в файле <stdlib.h>.
Пример формирования и печати массива с помощью ДСЧ:
#include<iostream.h>
#include<stdlib.h>
void main()
{
int a[100];
int n;
cout<<”\nEnter the size of array:”;cin>>n;
for(int I=0;I<n;I++)
{a[I]=rand()%100-50;
cout<<a[I]<<” “;
}
}
В этой программе используется перебор массива по одному элементу слева
направо с шагом 1.
Задача 1
Найти максимальный элемент массива.
#include<iostream.h>
#include<stdlib.h>
void main()
{
int a[100];
int n;
cout<<”\nEnter the size of array:”;cin>>n;
for(int I=0;I<n;I++)
{a[I]=rand()%100-50;
cout<<a[I]<<” “;
}
int max=a[0];
for(I=1;I<n;I++)
if (a[I]>max)max=a[I];
cout<<”\nMax=”<<max”;
}
В этой программе также используется перебор массива по одному элементу слева
направо с шагом 1.
Задача 2
Найти сумму элементов массива с четными индексами.
#include<iostream.h>
#include<stdlib.h>
void main()
{
int a[100];
int n;
cout<<”\nEnter the size of
array:”;cin>>n;
for(int I=0;I<n;I++)
{a[I]=rand()%100-50;
cout<<a[I]<<” “;
}
int Sum=0;
for(I=0;I<n;I+=2)
Sum+=a[I];//элементы с индексами 0,
2, 4… cout<<”\nSum=”<<Sum”;
}
Ввод массива
//Второй способ
for(I=0;I<n;I++)
if(I%2==0)Sum+=a[I]; ];//элементы с
индексами 0, 2, 4…
cout<<”\nSum=”<<Sum”;
Перебор массива по два элемента
1) Элементы массива можно обрабатывать по два элемента, двигаясь с обеих сторон
массива к его середине:
int I=0, J=N-1;
while( I<J)
{обработка a[I] и a[J];I++;J--;}
2) Элементы массива можно обрабатывать по два элемента, двигаясь от начала к концу с
шагом 1(т. е. обрабатываются пары элементов a[1]и a[2], a[2]и a[3] и т. д.):
for (I=1;I<N;I++)
{обработка a[I] и a[I+1]}
3) Элементы массива можно обрабатывать по два элемента, двигаясь от начала к концу с
шагом 2 (т. е. обрабатываются пары элементов a[1]и a[2], a[3]и a[4] и т. д.)
int I=1;
while (I<N-1 )
{обработка a[I] и a[I+1];
I+=2;}
8.3. Сортировка массивов
Сортировка – это процесс перегруппировки заданного множества объектов в
некотором установленном порядке.
Сортировки массивов подразделяются по быстродействию. Существуют простые
методы сортировок, которые требуют n*n сравнений, где n – количество элементов
массива и быстрые сортировки, которые требуют n*ln(n) сравнений. Простые методы
удобны для объяснения принципов сортировок, т. к. имеют простые и короткие
алгоритмы. Усложненные методы требуют меньшего числа операций, но сами операции
более сложные, поэтому для небольших массивов простые методы более эффективны.
Простые методы подразделяются на три основные категории:
сортировка методом простого включения;
сортировка методом простого выделения;
сортировка методом простого обмена;
Сортировка методом простого включения (вставки)
Элементы массива делятся на уже готовую последовательность и исходную.
При каждом шаге, начиная с I=2, из исходной последовательности извлекается I-ый
элемент и вставляется на нужное место готовой последовательности, затем I
увеличивается на 1 и т. д.
4
5 1 4 9 1
4
5 2 2 4 8
г
исходная
отовая
В процессе поиска нужного места осуществляются пересылки элементов больше
выбранного на одну позицию вправо, т. е. выбранный элемент сравнивают с очередным
элементом отсортированной части, начиная с J:=I-1. Если выбранный элемент больше a[I],
то его включают в отсортированную часть, в противном случае a[J] сдвигают на одну
позицию, а выбранный элемент сравнивают со следующим элементом отсортированной
последовательности. Процесс поиска подходящего места заканчивается при двух
различных условиях:
если найден элемент a[J]>a[I];
достигнут левый конец готовой последовательности.
int i,j,x;
for(i=1;i<n;i++)
{
x=a[i];//запомнили элемент, который будем вставлять
j=i-1;
while(x<a[j]&&j>=0)//поиск подходящего места
{
a[j+1]=a[j];//сдвиг вправо
j--;
}
a[j+1]=x;//вставка элемента
}
Сортировка методом простого выбора
Выбирается минимальный элемент массива и меняется местами с первым
элементом массива. Затем процесс повторяется с оставшимися элементами и т. д.
4 5 1
4 9 1
4 5 2
2 4 8
1
м
ин
int i,min,n_min,j;
for(i=0;i<n-1;i++)
{
min=a[i];n_min=i;//поиск минимального
for(j=i+1;j<n;j++)
if(a[j]<min){min=a[j];n_min=j;}
a[n_min]=a[i];//обмен
a[i]=min;
}
Сортировка методом простого обмена
Сравниваются и меняются местами пары элементов, начиная с последнего. В
результате самый маленький элемент массива оказывается самым левым элементом
массива. Процесс повторяется с оставшимися элементами массива.
4 5 1 4 9 1
4 5 2 2 4 8
for(int i=1;i<n;i++)
for(int j=n-1;j>=i;j--)
if(a[j]<a[j-1])
{int r=a[j];a[j]=a[j-1];a[j-1]=r;}
}
8.4. Поиск в отсортированном массиве
В отсортированном массиве используется дихотомический (бинарный) поиск. При
последовательном поиске требуется в среднем n/2 сравнений, где n – количество
элементов в массиве. При дихотомическом поиске требуется не более m сравнений, если
n- m-ая степень 2, если n не является степенью 2, то n<k=2m.
Массив делится пополам S:=(L+R)/ 2+1 и определяется в какой части
массива находится нужный элемент Х. Т .к. массив упорядочен, то если a[S]<X, то
искомый элемент находится в правой части массива, иначе - находится в левой части.
Выбранную часть массива снова надо разделить пополам и т. д., до тех пор, пока границы
отрезка L и R не станут равны.
1 3 8 1 1 1 1 2 2 3
0 1 5 9 1 3 7
0 1 2 3 4 5 6 7 8 9
L
S
R
S=(L+R)/2=4
int b;
cout<<"\nB=?";cin>>b;
int l=0,r=n-1,s;
do
{
s=(l+r)/2;//средний элемент
if(a[s]<b)l=s+1;//перенести леую границу
else r=s;//перенести правую границу
}while(l!=r);
if(a[l]==b)return l;
else return -1;
Лекция 9. Указатели
9.1. Понятии указателя
Указатели являются специальными объектами в программах на Си++. Указатели
предназначены для хранения адресов памяти.
Пример: Когда компилятор обрабатывает оператор определения переменной,
например, int i=10;, то в памяти выделяется участок памяти в соответствии с типом
переменной (int=> 4байта) и записывает в этот участок указанное значение. Все
обращения к этой переменной компилятор заменит на адрес области памяти, в которой
хранится эта переменная.
а10
&a
Программист может определить собственные переменные для хранения адресов
областей памяти. Такие переменные называются указателями. Указатель не является
самостоятельным типом, он всегда связан с каким-то другим типом.
Указатели делятся на две категории: указатели на объекты и указатели на функции.
Рассмотрим указатели на объекты, которые хранят адрес области памяти, содержащей
данные определенного типа .
В простейшем случае объявление указателя имеет вид:
тип *имя;
Тип может быть любым, кроме ссылки.
Примеры:
int *i;
double *f, *ff;
char *c;
Размер указателя зависит от модели памяти. Можно определить указатель на
указатель: int**a;
Указатель может быть константой или переменной, а также указывать на константу
или переменную.
Примеры:
1. int i;
//целая переменная
const int ci=1; //целая константа
int *pi;
//указатель на целую переменную
const int *pci;//указатель на целую константу
Указатель можно сразу проинициализировать:
int *pi=&i; //указатель на целую переменную
const int *pci=&ci;;//указатель на целую константу
2. int*const cpi=&i;//указатель-константа на целую переменную
const int* const cpc=&ci;//указатель-константа на целую константу
Если модификатор const относится к указателю (т. е. находится между именем
указателя и * ), то он запрещает изменение указателя, а если он находится слева от типа (т.
е. слева от * ), то он запрещает изменение значения, на которое указывает указатель.
Для инициализации указателя существуют следующие способы:
1)
Присваивание адреса существующего объекта:
a
1) с помощью операции получения адреса
5
int a=5;
*p
int *p=&a; или int p(&a);
2) с помощью проинициализированного указателя
int *r=p;
*r
&a
3) адрес присваивается в явном виде
char*cp=(char*)0х В800 0000;
где 0х В800 0000 – шестнадцатеричная константа, (char*) – операция приведения
типа.
4) присваивание пустого значения:
int*N=NULL;
int *R=0;
9.2. Операции с указателями
С указателями можно выполнять следующие операции:
1) разыменование (*);
2) присваивание;
3) арифметические
операции
(сложение
с
константой,
вычитание,
инкремент ++, декремент --);
4) сравнение;
5) приведение типов.
1) Операция разыменования предназначена для получения значения переменной или
константы, адрес которой хранится
в указателе.
Если указатель указывает на
переменную, то это значение можно изменять, также используя операцию разыменования.
Примеры:
int a; //переменная типа int
int*pa=new int; //указатель и выделение памяти под динамическую переменную
*pa=10;//присвоили значение динамической переменной, на которую указывает
указатель
a=*pa;//присвоили значение переменной а
Присваивать значение указателям-константам запрещено.
2) Приведение типов
На одну и ту же область памяти могут ссылаться указатели разного типа. Если
применить к ним операцию разыменования, то получатся разные результаты.
int a=123;
int*pi=&a;
char*pc=(char*)&a;
float *pf=(float*)&a;
printf("\n%x\t%i",pi,*pi);
printf("\n%x\t%c",pc,*pc);
printf("\n%x\t%f",pf,*pf);
При выполнении этой программы получатся следующие результаты:
66fd9c 123
66fd9c {
66fd9c 0.000000
Т. е. адрес у трех указателей один и тот же, но при разыменовании получаются
разные значения в зависимости от типа указателя.
В примере при инициализации указателя была использована операция приведения
типов. При использовании в выражении указателей разных типов, явное преобразование
требуется для всех типов, кроме void*. Указатель может неявно преобразовываться в
значения типа bool, при этом ненулевой указатель преобразуется в true, а нулевой в false.
3) Арифметические операции применимы только к указателям одного типа.
Инкремент увеличивает значение указателя на величину sizeof(тип).
Например:
char *pc;
int *pi;
float *pf;
.....
pc++;//значение увеличится на 1
pi++;//значение увеличится на 4
pf++;//значение увеличится на 4
Декремент уменьшает значение указателя на величину sizeof(тип).
Разность двух указателей – это разность их значений, деленная на размер
типа в байтах.
Например:
int a=123,b=456,c=789;
int*pi1=&a;
int *pi2=&b;
int*pi3=&c;
printf("\n%x",pi1-pi2);
printf("\n%x",pi1-pi3);
Результат
1
2
Суммирование двух указателей не допускается.
Можно суммировать указатель и константу:
pi3=pi3+2;
pi2=pi2+1;
printf("\n%x\t%d",pi1,*pi1);
printf("\n%x\t%d",pi2,*pi2);
printf("\n%x\t%d",pi3,*pi3);
Результат выполнения программы:
66fd9c 123
66fd9c 123
66fd9c 123
При записи выражений с указателями требуется обращать внимание на
приоритеты операций.
Лекция 10. Ссылки
10.1. Понятие ссылки
Ссылка – это синоним имени объекта, указанного при инициализации ссылки.
Формат объявления ссылки
тип & имя =имя_объекта;
Примеры:
int x;// определение переменной
int& sx=x;// определение ссылки на переменную х
const char& CR=’\n’;//определение ссылки на константу
Правила работы со ссылками:
1) Переменная ссылка должна явно инициализироваться при ее описании, если она не
является параметром функции, не описана как extern или не ссылается на поле
класса.
2) После инициализации ссылке не может быть присвоено другое значение.
3) Не существует указателей на ссылки, массивов ссылок и ссылок на ссылки.
4) Операция над ссылкой приводит к изменению величины на которую она ссылается
Ссылка не занимает дополнительного пространства в памяти, она является просто
другим именем объекта.
Пример1:
#include <iostream.h>
void main()
{
int I=123;
int &si=I;
cout<<”\ni=”<<I<<” si=”<<si;
I=456;
cout<<”\ni=”<<I<<” si=”<<si;
I=0; cout<<”\ni=”<<I<<” si=”<<si;
}
Выведется
I=123 si=123
I=456 si=456
I=0 si=0
Лекция 11. Указатели и массивы
11.1. Одномерные массивы и указатели
При определении массива ему выделяется память. После этого имя массива
воспринимается как константный указатель того типа, к которому относятся элементы
массива. Исключением является использовании операции sizeof (имя_массива) и
операции &имя_массива.
Примеры:
int a[100];
int k=sizeof(a);// результатом будет 4*100=400 (байтов).
int n=sizeof(a)/sizeof(a[0]);//количество элементов массива
Результатом операции & является адрес нулевого элемента массива:
имя_массива==&имя_массива=&имя_массива[0]
Имя массива является указателем-константой, значением которой служит адрес
первого элемента массива, следовательно, к нему применимы все правила адресной
арифметики, связанной с указателями. Запись имя_массива[индекс] это выражение с
двумя операндами: имя массива и индекс. Имя_массива - это указатель константа, а
индекс определяет смещение от начала массива. Используя указатели, обращение по
индексу можно записать следующим образом: *(имя_массива+индекс).
Пример:
for(int i=0;i<n;i++)//печать массива
cout<<*(a+i)<<" ";//к имени адресу массива добавляется константа i и полученное
//значение разыменовывается
Так как имя массива является константным указателем, то его невозможно
изменить, следовательно, запись *(а++) будет ошибочной, а *(а+1) - нет.
Указатели можно использовать и при определении массивов:
int a[100]={1,2,3,4,5,6,7,8,9,10};
int * na=a;//поставили указатель на уже определенный массив
int b=new int[100];//выделили в динамической памяти место под массив из 100
элементов
11.2. Многомерные массивы и указатели
Многомерный массив это массив, элементами которого служат массивы.
Например, массив с описанием int a[4][5] – это массив из 4 указателей типа int*, которые
содержат адреса одномерных массивов из 5 целых элементов (см. рис.).
int **a
Элементы
типа *int
0
1
2
3
0
0
0
0
1
1
1
1
2
2
2
2
3
3
3
3
4
4
4
4
*(a+1) – адрес строки массива с номером 1
Рис.
Инициализация многомерных массивов выполняется аналогично одномерным
массивам. Примеры:
int a[3][4] = {{11,22,33,44},{55,66,77,88},{99,110,120,130}};//проинициализированы
все //элементы массива
int b[3][4] = {{1},{2},{3}};//проинициализированы первые элементы каждой строки
int c[3][2]={1,2,3,4,5,6};//проинициализированы все элементы массива
Доступ к элементам многомерных массивов возможен и с помощью
индексированных переменных и с помощью указателей:
a[1][1] – доступ с помощью индексированных переменных,
*(*(a+1)+1) – доступ к этому же элементу с помощью указателей (см. рис.).
Лекция 12. Символьная информация и строки
Для символьных данных в Си++ введен тип char. Для представления символьной
информации используются символы, символьные переменные и текстовые константы.
Примеры:
const char c=’c’; //символ – занимает один байт, его значение не меняется
char a,b;//символьные переменные, занимают по одному байту, значения меняются
const char *s=“Пример строки\n” ;//текстовая константа
Строка в Си++ - это массив символов, заканчивающийся нуль-символом – ‘\0’
(нуль-терминатором). По положению нуль-терминатора определяется фактическая длина
строки. Количество элементов в таком массиве на 1 больше, чем изображение строки.
A
0
“A”
стро
ка (2байта)
\
A
‘
A’
с
имвол
(1байт)
Рис.2. Представление строки и символа
Присвоить значение строке с помощью оператора присваивания нельзя. Поместить
строку в массив можно либо при вводе, либо с помощью инициализации.
Пример
void main()
{
char s1[10]="string1";
int k=sizeof(s1);
cout<<s1<<"\t"<<k<<endl;
char s2[]="string2";
k=sizeof(s2);
cout<<s2<<"\t"<<k<<endl;
char s3[]={‘s’,’t’,’r’,’i’,’n’,’g’,’3’}
k=sizeof(s3);
cout<<s3<<"\t"<<k<<endl;
char *s4="string4";//указатель на строку, ее нельзя изменить
k=sizeof(s4);
cout<<s4<<"\t"<<k<<endl;
}
Результаты:
string1 10 – выделено 10 байтов, в том числе под \0
string2 8 – выделено 8 байтов (7+1байт под \0)
string3 8 – выделено 8 байтов (7+1байт под \0)
string4 4 – размер указателя
Примеры:
char *s=”String5”; - выделяется 8 байтов для строки
char*ss; - описан указатель
ss=”String6”;//память не выделяется , поэтому программа может закончиться
аварийно.
char *sss=new char[10];//выделяем динамическую память
strcpy(sss,”String7”);//копируем строку в память
Для ввода и вывода символьных данных в библиотеке языка СИ определены
следующие функции:
int getchar(void) - осуществляет вод одного символа их входного потока, при этом
она возвращает один байт информации (символ) в виде значения типа int. Это сделано для
распознавания ситуации, когда при чтении будет достигнут конец файла.
int putchar (int c) – помещает в стандартный выходной поток символ c.
char* gets(char*s) – считывает строку s из стандартного потока до появления
символа ‘\n’, сам символ ‘\n’ в строку не заносится.
int puts(const char* s) записывает строку в стандартный поток, добавляя в конец
строки символ ‘\n’, в случае удачного завершения возвращает значение больше или
равное 0 и отрицательное значение (EOF=-1) в случае ошибки.
Примеры:
1. char s[20];
cin>>s;//ввод строки из стандартного потока
cout<<s;//вывод строки в стандартный поток
Результат работы программы:
При вводе строки “123 456 789”, чтение байтов осуществляется до первого
пробела, т. е. в строку s занесется только первое слово строки “123/0”, следовательно,
выведется: 123.
2. char s[20];
gets(s);//ввод строки из стандартного потока
puts(s);//вывод строки в стандартный поток
Результат работы программы:
При вводе строки “123 456 789”, чтение байтов осуществляется до символа ‘\n’, т.
е. в s занесется строка”123 456 789\n\0”, при выводе строки функция puts возвращает еще
один символ ‘\n’, следовательно, будет выведена строка “123 456 789\n\n”.
3. char s[20];
scanf(“%s”,s);//ввод строки из стандартного потока
printf(“%s”,s);//вывод строки в стандартный поток
Результат работы программы:
При вводе строки “123 456 789”, чтение байтов осуществляется до первого
пробела, т. е. в строку s занесется только первое слово строки “123/0”, следовательно,
выведется: 123. Т. к. s – имя массива, т. е. адрес его первого элемента, операция & в
функции scanf не используется.
Для работы со строками существуют специальные библиотечные функции,
которые содержатся в заголовочном файле string.h. Рассмотрим некоторые из этих
функций:
Прототип
функции
unsigned
strlen(const char*s);
int strcmp(const
char*s1, const char *s2);
Краткое описание
Примечание
Вычисляет длину строки s.
Сравнивает строки s1 и s2.
Если
s1<s2,
то
результат
отрицательный,
если s1==s2, то результат
равен 0, если s2>s1 – результат
положительный.
Если
s1<s2,
то
результат
отрицательный,
если s1==s2, то результат
равен 0, если s2>s1 – результат
положительный.
int strcnmp(const
char*s1, const char *s2);
Сравнивает первые n
символов строк s1 и s2.
char*strcpy(char*s
1, const char*s2);
char*strncpy(char
*s1, const char*s2, int n);
Копирует символы строки s1
в строку s2.
Копирует n символов строки
Конец
строки
s1 в строку s2.
отбрасывается
или
дополняется пробелами.
Приписывает строку s2 к
строке s1
Приписывает
первые
n
символов строки s2 к строке s1
Выделяет память и переносит
При выделении памяти
в нее копию строки s
используются функции
char*strcat(char*s
1, const char*s2);
char*strncat(char*
s1, const char*s2);
char*strdup(const
char*s);
Пример1:
Дана строка символов, состоящая из слов, слова разделены между собой
пробелами. Удалить из строки все слова, начинающиеся с цифры.
#include <stdio.h>
#include <string.h>
void main()
{
char s[250], //исходная строка
w[25], //слово
mas[10][25];//массив слов
puts(“\nвведите строку”);
gets(s);
int k=0,t=0,i,len,j;
len=strlen(s);
while(t<len)
{
for(j=0,i=t;s[i]!=’ ‘;i++,j++)w[j]=s[i];//формируем слово до пробела
w[j]=’/0’;//формируем конец строки
strcpy(mas[k],w);//копируем слово в массив
k++;//увеличиваем счетчик слов
t=i+1;//переходим к следующему слову в исходной строке
}
strcpy(s,””);//очищаем исходную строку
for(t=0;t<k;t++)
if(mas[t][0]<’0’&&mas[t][0]>’9’)//если первый символ не цифра
{
strcat(s,mas[t]);//копируем в строку слово
strcat(s,” “);//копируем в строку пробел
}
puts(s);//выводим результат
}
Пример2:
Сформировать динамический массив строк. Удалить из него строку с заданным
номером.
#include <iostream.h>
#include <string.h>
void main()
{
int n;
cout<<"\nN=?";cin>>n;
char s[100];
char**matr=new char*[n];
for(int i=0;i<n;i++)
{
cout<<"\nS=?";
cin>>s;
matr[i]=new char[strlen(s)];
strcpy(matr[i],s);
}
for(i=0;i<n;i++)
{
cout<<matr[i];
cout<<"\n";
}
int k;
cout<<"\nK=?";
cin>>k;
if(k>=n){cout<<"There is not such string\n";return;}
char **temp=new char*[n-1];
int j=0;
for(i=0;i<n;i++)
if(i!=k)
{
temp[j]=new char[strlen(matr[i])];
strcpy(temp[j],matr[i]);
j++;
}
n--;
for(i=0;i<n;i++)
{
cout<<temp[i];
cout<<"\n";
}
}
Лекция 13. Функции в Си++
С увеличением объема программы становится невозможно удерживать в памяти
все детали. Чтобы уменьшить сложность программы, ее разбивают на части. В Си++
задача может быть разделена на более простые подзадачи с помощью функций.
Разделение задачи на функции также позволяет избежать избыточности кода, т. к.
функцию записывают один раз, а вызывают многократно. Программу, которая содержит
функции, легче отлаживать.
Часто используемые функции можно помещать в библиотеки. Таким образом,
создаются более простые в отладке и сопровождении программы.
13. 1. Объявление и определение функций
Функция – это именованная последовательность описаний и операторов,
выполняющая законченное действие, например, формирование массива, печать массива и
т. д.
Функция, во-первых, является одним из производных типов СИ++, а ,во-вторых,
минимальным исполняемым модулем программы.
Исх. данные (параметры,
передаваемые в функцию)
Функция
Результат (возвращаемое
значение)
Любая функция должна быть объявлена и определена.
Объявление функции (прототип, заголовок) задает имя функции, тип
возвращаемого значения и список передаваемых параметров.
Определение функции содержит, кроме объявления, тело функции, которое
представляет собой последовательность описаний и операторов.
тип имя_функции([список_формальных_параметров])
{ тело_функции}
Тело_функции – это блок или составной оператор. Внутри функции нельзя
определить другую функцию.
В теле функции должен быть оператор, который возвращает полученное значение
функции в точку вызова. Он может иметь 2 формы:
1)
return выражение;
2)
return;
Первая форма используется для возврата результата, поэтому выражение должно
иметь тот же тип, что и тип функции в определении. Вторая форма используется, если
функция не возвращает значения, т. е. имеет тип void. Программист может не
использовать этот оператор в теле функции явно, компилятор добавит его автоматически
в конец функции перед }.
Тип возвращаемого значения может быть любым, кроме массива и функции, но
может быть указателем на массив или функцию.
Список формальных параметров – это те величины, которые требуется передать в
функцию. Элементы списка разделяются запятыми. Для каждого параметра указывается
тип и имя. В объявлении имена можно не указывать.
Для того, чтобы выполнялись операторы, записанные в теле функции, функцию
необходимо вызвать. При вызове указываются: имя функции и фактические параметры.
Фактические параметры заменяют формальные параметры при выполнении операторов
тела функции. Фактические и формальные параметры должны совпадать по количеству и
типу.
Объявление функции должно находиться в тексте раньше вызова функции, чтобы
компилятор мог осуществить проверку правильности вызова. Если функция имеет тип не
void, то ее вызов может быть операндом выражения.
Пример:
Заданы координаты сторон треугольника. Если такой треугольник существует, то
найти его площадь.
1. Математическая модель:
1)
l=sqrt(pow(x1-x2,2)+pow(y1-y2,2));//длина стороны треугольника
2)
p=(a+b+c)/2;
s=sqrt(p*(p-a)*(p-b)*(p-c));//формула Герона
3)
проверка существования треугольника
(a+b>c&&a+c>b&&c+b>a)
2. Алгоритм:
1) Ввести координаты сторон треугольника (х1,у1),(х2,у2),(х3,у3);
2) Вычислить длины сторон ab, bc, ca;
3) Проверить существует ли треугольник с такими сторонами. Если да, то
вычислить площадь и вывести результат.
4) Если нет, то вывести сообщение.
5) Если все координаты равны 0, то конец, иначе возврат на п.1.
#include <iostream.h>
#include <math.h>
double line(double x1,double y1,double x2,double y2)
{
//функция возвращает длину отрезка, заданного координатами x1,y1 и x2,y2
return sqrt(pow(x1-x2,2)+pow(y1-y2,2));
}
double square(double a, double b, double c)
{
//функция возвращает площадь треугольника, заданного длинами сторон а,b,c
double s, p=(a+b+c)/2;
return s=sqrt(p*(p-a)*(p-b)*(p-c));//формула Герона
}
bool triangle(double a, double b, double c)
{
//возвращает true, если треугольник существует
if(a+b>c&&a+c>b&&c+b>a) return true;
else return false;
}
void main()
{
double x1=1,y1,x2,y2,x3,y3;
double point1_2,point1_3,point2_3;
do
{
cout<<"\nEnter koordinats of triangle:";
cin>>x1>>y1>>x2>>y2>>x3>>y3;
point1_2=line(x1,y1,x2,y2);
point1_3=line(x1,y1,x3,y3);
point2_3=line(x2,y2,x3,y3);
if(triangle(point1_2,point1_3,point2_3)==true)
cout<<"S="<<square(point1_2,point2_3,point1_3)<<"\n";
else cout<<"\nTriagle doesnt exist";
}
while(!(x1==0&&y1==0&&x2==0&&y2==0&&x3==0&&y3==0));
}
13.2.Параметры функции
Основным способом обмена информацией между вызываемой и вызывающей
функциями является механизм параметров. Существует два способа передачи параметров
в функцию: по адресу и по значению.
При передаче по значению выполняются следующие действия:
вычисляются значения выражений, стоящие на месте фактических
параметров;
в стеке выделяется память под формальные параметры функции;
каждому фактическому параметру присваивается значение формального
параметра, при этом проверяются соответствия типов и при необходимости выполняются
их преобразования.
Пример:
double square(double a, double b, double c)
{
//функция возвращает площадь треугольника, заданного длинами сторон а,b,c
double s, p=(a+b+c)/2;
return s=sqrt(p*(p-a)*(p-b)*(p-c));//формула Герона
}
1) double s1=square(2.5,2,1);
2) double a=2.5,b=2,c=1;
double s2=square(a,b,c);
3) double x1=1,y1=1,x2=3,y2=2,x3=3,y3=1;
double s3=square(sqrt(pow(x1-x2,2)+pow(y1-y2,2)),//расстояние между 1и2
sqrt(pow(x1-x3,2)+pow(y1-y3,2)), //расстояние между 1 и 3
sqrt(pow(x3-x2,2)+pow(y3-y2,2)));//расстояние между 2 и3
Стек
A 2
.5
B
C
S
P
2
1
P и S – локальные переменные.
Т. о. в стек заносятся копии фактических параметров и операторы функции
работают с этими копиями. Доступа к самим фактическим параметрам у функции нет,
следовательно нет возможности их изменить.
При передаче по адресу в стек заносятся копии адресов параметров,
следовательно, у функции появляется доступ к ячейке памяти, в которой находится
фактический параметр и она может его изменить.
Пример.
void Change(int a,int b)//передача по значению
{int r=a;a=b;b=r;}
int x=1,y=5;
Change(x,y);
A 1
B 5
r
5
1
1
cout<<”x=”<<x<<”y=”<<y;
выведется: x=1y=5
void Change(int *a,int *b)//передача по адресу
{int r=*a;*a=*b;*b=r;}
int x=1,y=5;
Change(&x,&y);
A &
5
B
1
x
&
y
r
1
cout<<”x=”<<x<<”y=”<<y;
выведется: x=5y=1
Для передачи по адресу также могут использоваться ссылки. При передаче по
ссылке в функцию передается адрес указанного при вызове параметра, а внутри функции
все обращения к параметру неявно разыменовываются.
void Change(int &a,int &b)
{int r=a;a=b;b=r;}
int x=1,y=5;
Change(x,y);
A &
5
B
1
x
&
y
r
1
cout<<”x=”<<x<<”y=”<<y;
выведется: x=5y=1
Использование ссылок вместо указателей улучшает читаемость программы, т. к. не
надо применять операцию разыменовывания. Использование ссылок вместо передачи по
значению также более эффективно, т .к. не требует копирования параметров. Если
требуется запретить изменение параметра внутри функции, используется модификатор
const. Рекомендуется ставить const перед всеми параметрами, изменение которых в
функции не предусмотрено (по заголовку будет понятно, какие параметры в ней будут
изменяться, а какие нет).
13.3. Локальные и глобальные переменные
Переменные, которые используются внутри данной функции, называются
локальными. Память для них выделяется в стеке, поэтому после окончания работы
функции они удаляются из памяти. Нельзя возвращать указатель на локальную
переменную, т. к. память, выделенная такой переменной будет освобождаться.
int*f()
{
int a;
....
return&a;// НЕВЕРНО
}
Глобальные переменные – это переменные, описанные вне функций. Они видны во
всех функциях, где нет локальных переменных с такими именами.
Пример:
int a,b;//глобальные переменные
void change()
{
int r;//локальная переменная
r=a;a=b;b=r;
}
void main()
{
cin>>a,b;
change();
cout<<”a=”<<a<<”b=”<<b;
}
Глобальные переменные также можно использовать для передачи данных между
функциями, но этого не рекомендуется делать, т. к. это затрудняет отладку программы и
препятствует помещению функций в библиотеки. Нужно стремиться к тому, чтобы
функции были максимально независимы, а их интерфейс полностью определялся
прототипом функции.
Лекция 14. Функции с начальными (умалчиваемыми) значениями параметров
В определении функции может содержаться начальное (умалчиваемое) значение
параметра. Это значение используется, если при вызове функции соответствующий
параметр опущен. Се параметры, описанные справа от такого параметра также должны
быть умалчиваемыми.
Пример:
void print(char*name=”Номер дома: ”,int value=1)
{cout<<”\n”<<name<<value;}
Вызовы:
1. print();
Вывод: Номер дома: 1
2. print(“Номер квартиры”,15);
Вывод: Номер квартиры: 15
3. print(,15); - ошибка, т. к. параметры можно пускать только с конца
Поэтому функцию лучше переписать так:
void print(int value=1, char*name=”Номер дома: ”)
{cout<<”\n”<<name<<value;}
Вызовы:
1. print();
Вывод: Номер дома: 1
2. print(15);
Вывод: Номер дома: 15
3. print(6, “Размерность пространства”);
Вывод: Размерность пространства: 6
Подставляемые (inline) функции
Некоторые функции в СИ++ можно определить с использованием служебного
слова inline. Такая функция называется подставляемой или встраиваемой.
Например:
inline float Line(float x1,float y1,float x2=0, float y2=0)
{return sqrt(pow(x1-x2)+pow(y1-y2,2));}//функция возвращает расстояние от точки с
координатами(x1,y1)(по умолчанию центр координат) до точки с координатами (x2,y2).
Обрабатывая каждый вызов подставляемой функции, компилятор пытается
подставить в текст программы код операторов ее тела. Спецификатор inline определяет
для функции так называемое внутреннее связывание, которое заключается в том, что
компилятор вместо вызова функции подставляет команды ее кода. При этом может
увеличиваться размер программы, но исключаются затраты на передачу управления к
вызываемой функции и возврата из нее. Подставляемые функции используют, если тело
функции состоит из нескольких операторов.
Подставляемыми не могут быть:
рекурсивные функции;
функции, у которых вызов размещается до ее определения;
функции, которые вызываются более одного раза в выражении;
функции, содержащие циклы, переключатели и операторы переходов;
функции, которые имеют слишком большой размер, чтобы сделать
подстановку.
Функции с переменным числом параметров
В СИ++ допустимы функции, у которых при компиляции не фиксируется число
параметров, и , кроме того может быть неизвестен тип этих параметров. Количество и тип
параметров становится известным только в момент вызова, когда явно задан список
фактических параметров. Каждая функция с переменным числом параметров должна
иметь хотя бы один обязательный параметр. Определение функции с переменным числом
параметров:
тип имя (явные параметры,. . . )
{тело функции }
После списка обязательных параметров ставится запятая, а затем многоточие,
которое показывает, что дальнейший контроль соответствия количества и типов
параметров при обработке вызова функции производить не нужно. Сложность
заключается в определении начала и конца списка параметров, поэтому каждая функция с
переменным числом параметров должна иметь механизм определения количества и типов
параметров. Существует два подхода:
1)
известно количество параметров, которое передается как
обязательный параметр;
2)
известен признак конца списка параметров;
Пример1
Найти среднее арифметическое последовательности чисел
//известен признак конца списка параметров
#include<iostream.h>
float sum(int k, . . .)
{
int *p=&k;//настроили указатель на параметр k
int s=0;
for(;k!=0;k--)
s+=*(++p);
return s/k;
}
void main()
{
cout<<”\n4+6=”<<sum(2,4,6);//находит среднее арифметическое 4+6
cout<<”\n1+2++3+4=”<<sum(4,1,2,3,4);//находит среднее арифметическое 1+2+3+4
}
Для доступа к списку параметров используется указатель *p типа int. Он
устанавливается на начало списка параметров в памяти, а затем перемещается по адресам
фактических параметров (++p).
Пример 2.
//известен признак конца списка параметров
#include<iostream.h>
int sum(int k, . . .)
{
int *p=&k;//настроили указатель на параметр k
int s=*p;//значение первого параметра присвоили s
for(int i=1;p!=0;i++)//пока нет конца списка
s+=*(++p);
return s/(i-1);
}
void main()
{
cout<<”\n4+6=”<<sum(4,6,0);//находит среднее арифметическое 4+6
cout<<”\n1+2++3+4=”<<sum(1,2,3,4,0);//находит среднее арифметическое 1+2+3+4
}
Лекция 15. Динамические структуры данных
Во многих задачах требуется использовать данные, у которых конфигурация,
размеры и состав могут меняться в процессе выполнения программы. Для их
представления используют динамические информационные структуры. К таким
структурам относят:
линейные списки;
стеки;
очереди;
бинарные деревья;
Они отличаются способом связи отдельных элементов и допустимыми
операциями. Динамическая структура может занимать несмежные участки
динамической памяти.
Наиболее
простой
динамической
структурой
является
линейный
однонаправленный список, элементами которого служат объекты структурного типа
(рис.4).
Информационное
поле
адресное
поле
Информационное
поле
Beg – указатель на начало списка
Рис.4. Линейный однонаправленный список
15.1. Линейный однонаправленный список
Описание простейшего элемента такого списка выглядит следующим образом:
struct имя_типа
{
информационное поле;
адресное поле;
};
Информационное поле – это поле любого, ранее объявленного или стандартного, типа;
адресное поле – это указатель на объект того же типа, что и определяемая структура, в
него записывается адрес следующего элемента списка.
NULL
Информационных полей может быть несколько.
Примеры.
1. struct Node
{
int key;//информационное поле
Node*next;//адресное поле
};
2. struct point
{
char*name;//информационное поле
int age;//информационное поле
point*next;//адресное поле
};
Каждый элемент списка содержит ключ, который идентифицирует этот элемент. Ключ
обычно бывает либо целым числом (пример 1), либо строкой (пример 2).
Над списками можно выполнять следующие операции:
начальное формирование списка (создание первого элемента);
добавление элемента в конец списка;
добавление элемента в начало списка;
удаление элемента с заданным номером;
чтение элемента с заданным ключом;
вставка элемента в заданное место списка (до или после элемента с
заданным ключом);
упорядочивание списка по ключу
и др.
Пример1. Создание и печать однонаправленного списка
#include <iostream.h>
#include<string.h>
//описание структуры
struct point
{char *name;//информационное поле
int age;//информационное поле
point*next;//адресное поле
};
point* make_point()
//создание одного элемента
{
point*p=new(point);//выделить память
char s[20];
cout<<"\nEnter the name:";
cin>>s;
p->name=new char[strlen(s)+1];//выделить память под динамическую строку
символов
strcpy(p->name,s);//записать информацию в строку символов
cout<<"\nEnter the age";
cin>>p->age;
p->next=0;//сформировать адресное поле
return p;
}
void print_point(point*p)
//печать информационных полей одного элемента списка
{
cout<<"\nNAME:"<<p->name;
cout<<"\nAGE:"<<p->age;
cout<<"\n--------------------------------\n";
}
point* make_list(int n)
//формирование списка из n элементов
{
point* beg=make_point();//сформировать первый элемент
point*r;
for(int i=1;i<n;i++)
{
r=make_point();//сформировать следующий элемент
//добавление в начало списка
r->next=beg;//сформировать адресное поле
beg=r;//изменить адрес первого элемента списка
}
return beg;//вернуть адрес начала списка
}
int print_list(point*beg)
//печать списка, на который указывает указатель beg
{
point*p=beg;//р присвоить адрес первого элемента списка
int k=0;//счетчик количества напечатанных элементов
while(p)//пока нет конца списка
{
print_point(p);//печать элемента, на который указывает элемент p
p=p->next;//переход к следующему элементу
k++;
}
return k;//количество элементов в списке
}
void main()
{
int n;
cout<<"\nEnter the size of list";
cin>>n;
point*beg=make_list(n);//формирование списка
if(!print_list(beg)) cout<<"\nThe list is empty";}//печать списка
Пример 2. Удаление из однонаправленного списка элемента с номером k (рис
5.).
beg
p
k
Рис. 5. Удаление элемента с номером k из однонаправленного списка
point*del_point(point*beg,int k)
//удаление элемента с номером к
{
point*p=beg,//поставить вспомогательную переменную на начало списка
*r;//вспомогательная переменная для удаления
int i=0;//счетчик элементов в списке
if(k==0)
{//удалить первый элемент
beg=p->next;
delete[]p->name;//удалить динамическое поле name
delete[]p;//удалить элемент из списка
return beg;//вернуть адрес первого элемента списка
}
while(p)//пока нет конца списка
{
if(i==k-1)//дошли до элемента с номером k-1, чтобы поменять его
поле next
{//удалить элемент
r=p->next;//поставить r на удаляемый элемент
if(r)//если p не последний элемент
{
p->next=r->next;//исключить r из списка
delete[]r->name;//удалить динамическое поле name
delete[]r;//удалить элемент из списка
}
else p->next=0;//если p -последний элемент, то в поле next присвоить
NULL
}
p=p->next;//переход к следующему элементу списка
i++;//увеличить счетчик элементов
}
return beg;//вернуть адрес первого элемента}
15.2. Работа с двунаправленным списком
beg
pred
key
next
Рис. 6 Двунаправленный список
Пример 3.
1. Создать двунаправленный список, выполнить удаление элемента с заданным
номером, добавление элемента с заданным номером, печать полученных списков.
#include <iostream.h>
struct point//описание структуры
{
int key;//ключевое поле
point* pred,*next;//адресные поля
};
point*make_list()
{
int n;
cout<<"n-?";cin>>n;
point *p,*r,*beg;
p=new (point);//создать первый элемент
beg=p;//запомнить адрес в переменную beg, в которой хранится начало списка
cout<<"key-?";cin>>p->key;//заполнить ключевое поле
p->pred=0;p->next=0;//запомнить адресные поля
for(int i=1;i<n;i++)//добавить элементы в конец списка
{
r=new(point);//новый элемент
cout<<"key-?";cin>>r->key;//адресное поле
p->next=r;//связать начало списка с r
r->pred=p;//связать r с началом списка
r->next=0;//обнулить последнее адресное поле
p=r;//передвинуть p на последний элемент списка
}
return beg;//вернуть первый элемент списка
}
void print_list(point *beg)
{
if (beg==0)//если список пустой
{
cout<<"The list is empty\n";
return;
}
point*p=beg;
while(p)//пока не конец списка
{
cout<<p->key<<"\t";
p=p->next;//перейти на следующий
}
cout<<"\n";
}
point* del_point(point*beg, int k)
{
point *p=beg;
if(k==0)//удалить первый элемент
{
beg=beg->next;//переставить начало списка на следующий элемент
if(beg==0)return 0;//если в списке только один элемент
beg->pred=0;//обнулить адрес предыдущего элемента
delete p;//удалить первый
return beg;//вернуть начало списка
}
//если удаляется элемент из середины списка
for(int i=0;i<k-1&&p!=0;i++,p=p->next);//пройти по списку либо до элемента с
предыдущим номером, либо до конца списка
if(p==0||p->next==0)return beg;//если в списке нет элемента с номером k
point*r=p->next;//встать на удаляемый элемент
p->next=r->next;//изменить ссылку
delete r;//удалить r
r=p->next;//встать на следующий
if(r!=0)r->pred=p;//если r существует, то связать элементы
return beg;//вернуть начало списка
}
point* add_point(point *beg,int k)
{
point *p;
p=new(point);//создать новый элемент и заполнить ключевое поле
cout<<"key-?";cin>>p->key;
if(k==0)//если добавляется первый элемент
{
p->next=beg;//добавить перед beg
p->pred=0;//обнулить адрес предыдущего
beg->pred=p;//связать список с добавленным элементом
beg=p;//запомнить первый элемент в beg
return beg;//вернуть начало списка
}
point*r=beg;//встать на начало списка
for(int i=0;i<k-1&&r->next!=0;i++,r=r->next);//пройти по списку либо до конца
списка, либо до элемента с номером k-1
p->next=r->next;//связать р с концом списка
if(r->next!=0)r->next->pred=p;//если элемент не последний, то связать конец списка
ср
p->pred=r;//связать р и r
r->next=p;
return beg;//вернуть начало списка
}
void main()
{
point*beg;
int i,k;
do
{
cout<<"1.Make list\n";
cout<<"2.Print list\n";
cout<<"3.Add point\n";
cout<<"4.Del point\n";
cout<<"5.Exit\n";
cin>>i;
switch(i)
{
case 1:
{beg=make_list();break;}
case 2:
{print_list(beg);break;}
case 3:
{
cout<<"\nk-?";cin>>k;
beg=add_point(beg,k);
break;
}
case 4:
{
cout<<"\nk-?";cin>>k;
beg=del_point(beg,k);
break;
}
}
}
while(i!=5);
}
15.3 Ввод-вывод в С
Файл – это именованная область внешней памяти. Файл имеет следующие
характерные особенности:
1.
имеет имя на диске, что дает возможность программам работать с
несколькими файлами;
2.
длина файла ограничивается только емкостью диска.
Особенностью С является отсутствие в этом языке структурированных файлов. Все
файлы рассматриваются как не структурированная последовательность байтов. При таком
подходе понятие файла распространяется и на различные устройства. Одни и те же
функции используются как для обмена данными с файлами, так и для обмена с
устройствами.
Библиотека С поддерживает три уровня ввода-вывода:
- потоковый ввод-вывод;
- ввод-вывод нижнего уровня;
- ввод-вывод для консоли портов (зависит от конкретной ОС).
Рассмотрим потоковый ввод-вывод.
Потоковый ввод-вывод
На уровне потокового ввода-вывода обмен данными производится побайтно, т. е.
за одно обращение к устройству (файлу) производится считывание или запись
фиксированной порции данных (512 или 1024 байта). При вводе с диска или при
считывании из файла данные помещаются в буфер ОС, а затем побайтно или порциями
передаются программе пользователя. При выводе в файл данные также накапливаются в
буфере, а при заполнении буфера записываются в виде единого блока на диск. Буферы ОС
реализуются в виде участков основной памяти. Т .о. поток – это файл вместе с
предоставленными средствами буферизации. Функции библиотеки С, поддерживающие
обмен данными на уровне потока позволяют обрабатывать данные различных размеров и
форматов. При работе с потоком можно:
1. Открывать и закрывать потоки (при этом указатели на поток связываются с
конкретными файлами);
2. Вводить и выводить строки, символы, форматированные данные, порции
данных произвольной длины;
3. Управлять буферизацией потока и размером буфера;
4. Получать и устанавливать указатель текущей позиции в файле.
Прототипы функций ввода-вывода находятся в заголовочном файле <stdio.h>,
который также содержит определения констант, типов и структур, необходимых для
обмена с потоком.
Открытие и закрытие потока
Прежде, чем начать работать с потоком, его надо инициировать, т. е. открыть. При
этом поток связывается со структурой предопределенного типа FILE, определение
которой находится в файле <stdio.h>. В структуре находится указатель на буфер,
указатель на текущую позицию и т. п. При открытии потока возвращается указатель на
поток, т. е. на объект типа FILE. Указатель на поток должен быть объявлен следующим
образом:
#include <stdio.h>
........
FILE*f;//указатель на поток
Указатель на поток приобретает значение в результате выполнения функции
открытия потока:
FILE* fopen(const char*filename,const char*mode);
где const char*filename – строка, которая содержит имя файла, связанного с
потоком,
const char*mode – строка режимов открытия файла.
Например:
f=fopen(“t.txt”,”r”);
где t.txt – имя файла, r – режим открытия файла.
Файл связанный с потоком можно открыть в одном из 6 режимов
Реж
Описание режима открытия файла
им
r
Файл открывается для чтения, если файл не существует , то выдается
ошибка при исполнении программы.
w
Файл открывается для записи, если файл не существует, то он будет
создан, если файл уже существует, то вся информация из него стирается.
a
Файл открывается для добавления, если фай не существует, то он
будет создан, если существует, то информация из него не стирается, можно
выполнять запись в конец файла
r+
Файл открывается для чтения и записи, изменить размер файла
нельзя, если файл не существует , то выдается ошибка при исполнении
программы.
w+
Файл открывается для чтения и записи, если файл не существует, то
он будет создан, если файл уже существует, то вся информация из него
стирается.
a+
Файл открывается для чтения и записи, если фай не существует, то он
будет создан, если существует, то информация из него не стирается, можно
выполнять запись в конец файла
Поток можно открывать в текстовом (t) или двоичном режиме(b). В текстовом
режиме поток рассматривается как совокупность строк, в конце каждой строки находится
управляющий символ ‘\n’. В двоичном режиме поток рассматривается как набор двоичной
информации. Текстовый режим устанавливается по умолчанию.
В файле stdio.h определена константа EOF, которая сообщает об окончании файла
(отрицательное целое число).
При открытии потока могут возникать следующие ошибки:
- файл, связанный с потоком не найден (при чтении из файла);
- диск заполнен (при записи);
- диск защищен от записи (при записи) и т. п.
В этих случаях указатель на поток приобретет значение NULL (0). Указатель на
поток, отличный от аварийного не равен 0.
Для вывода об ошибке при открытии потока используется стандартная
библиотечная функция из файла <stdio.h>
void perror (const char*s);
Эта функция выводит строку символов, не которую указывает указатель s, за этой
строкой размещается двоеточие пробел и сообщение об ошибке. Текст сообщения
выбирается на основании номера ошибки. Номер ошибки заносится в переменную int
errno(определена в заголовочном файле errno.h).
После того как файл открыт, в него можно записывать информацию или считывать
информацию, в зависимости от режима.
Открытые файлы после окончания работы рекомендуется закрыть явно. Для этого
используется функция:
int fclose(FILE*f);
Изменить режим работы с файлом можно только после закрытия файла.
Пример:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void main()
{
FILE *f;
char filename[20];
cout<<”\nEnter the name of file:”; cin>>filename;
if(f=fopen(filename,”rb”)==0)//открываем для чтения в бинарном режиме и
проверяем
// возникает ли ошибка при открытии файла
{
perror(strcat“error in file :”,filename);//strcat складывает две строки
exit(0);//выход из программы
}
.....
fclose(f);
}
Для текстового файла:
if(f=fopen(filename,”rt”)==0)//открываем для чтения и проверяем возникает ли
ошибка при //открытии файла
if(f=fopen(filename,”r”)==0)//открываем для чтения и проверяем возникает ли
ошибка при //открытии файла
Стандартные файлы и функции для работы с ними
Когда программа начинает выполняться, автоматически открываются несколько
потоков, из которых основными являются:
- стандартный поток ввода (stdin);
- стандартный поток вывода (stdout);
- стандартный поток вывода об ошибках (stderr).
По умолчанию stdin ставится в соответствие клавиатура, а потокам stdout и stderr монитор. Для ввода-вывода с помощью стандартных потоков используются функции:
- getchar()/putchar() – ввод-вывод отдельного символа;
- gets()/puts() – ввод-вывод строки;
- scanf()/printf() – форматированный ввод/вывод.
Функции рассматривались, когда мы рассматривали строковые и символьные
данные. Теперь мы можем связать их со стандартными потоками: ввод осуществляется из
стандартного потока stdin вывод осуществляется в стандартный поток stdout. Аналогично
работе со стандартными потоками выполняется ввод-вывод в потоки, связанные с
файлами.
Символьный ввод-вывод
Для символьного ввода-вывода используются функции:
- int fgetc(FILE*fp), где fp – указатель на поток, из которого выполняется
считывание. Функция возвращает очередной символ в форме int из потока fp. Если символ
не может быть прочитан, то возвращается значение EOF.
- int fputc(int c, FILE*fp), где fp – указатель на поток, в который выполняется
запись, c – переменная типа int, в которой содержится записываемый в поток символ.
Функция возвращает записанный в поток fp символ в форме int . Если символ не может
быть записан, то возвращается значение EOF.
Пример:
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *f;
char c;
char *filename=”f.txt”;
if((f=fopen(filename,”r”)==0)
{
perror(filename);exit(0);
}
while(c=fgetc(f)!=EOF)
putchar(c);//вывод с на стандартное устройство вывода
fclose(f);
}
Строковый ввод-вывод
Для построчного ввода-вывода используются следующие функции:
1)
char* fgets(char* s,int n,FILE* f), где
char*s – адрес, по которому размещаются считанные байты,
int n – количество считанных байтов,
FILE* f – указатель на файл, из которого производится считывание.
Прием байтов заканчивается после передачи n-1 байтов или при получении
управляющего символа ‘\n’. Управляющий символ тоже передается в принимающую
строку. Строка в любом случае заканчивается ‘\0’. При успешном завершении считывания
функция возвращает указатель на прочитанную строку, при неуспешном – 0.
2)
int puts(char* s, FILE* f), где
char*s – адрес, из которого берутся записываемые в файл байты,
FILE* f – указатель на файл, в который производится запись.
Символ конца строки (‘\0’) в файл не записывается. Функция возвращает EOF, если
при записи в файл произошла ошибка, при успешной записи возвращает неотрицательное
число.
Пример:
//копирование файла in в файл out
int MAXLINE=255;//максимальная длина строки
FILE *in,//исходный файл
*out;//принимающий файл
char* buf[MAXLINE];//строка, с помощью которой выполняется копирование
in=fopen(“f1.txt”,”r”);//открыть исходный файл для чтения
out=fopen(“f2.txt”,”w”);//открыть принимающий файл для записи
while(fgets(buf,MAXLINE,in)!=0)//прочитать байты из файла in в строку buf
fputs(buf,out);//записать байты из строки buf в файл out
fclose(in);fclose(out);//закрыть оба файла
Блоковый ввод-вывод
Для блокового ввода-вывода используются функции:
1)
int fread(void*ptr,int size, int n, FILE*f), где
void*ptr – указатель на область памяти, в которой размещаются считанные из файла
данные,
int size – размер одного считываемого элемента,
int n – количество считываемых элементов,
FILE*f – указатель на файл, из которого производится считывание.
В случае успешного считывания функция возвращает количество считанных
элементов, иначе – EOF.
2)
int fwrite(void*ptr,int size, int n, FILE*f), где
void*ptr – указатель на область памяти, в которой размещаются считанные из файла
данные,
int size – размер одного записываемого элемента,
int n – количество записываемых элементов,
FILE*f – указатель на файл, в который производится запись.
В случае успешной записи функция возвращает количество записанных элементов,
иначе – EOF.
Пример:
struct Employee
{
char name[30];
char title[30];
float rate;
};
void main()
{
Employee e;
FILE *f;
if((f=fopen(“f.dat”,”wb”))==NULL)
{
cout<<”\nCannot open file for writing”;
exit(1);
}
int n;
//запись в файл
printf(“\nN-?”);
scanf(“%d”,&n);
for(int i=0;i<n;i++)
{
//формируем структуру е
printf(“\nname:”);scanf(“%s”,&e.name);
printf(“\ntitle:”);scanf(“%s”,&e.title);
printf(“\nrate:”);scanf(“%s”,&e.rate);
//записываем е в файл
fwrite(&e,sizeof(Employee),1,f);
}
fclose(f);
//чтение из файла
if((f=fopen(“f.dat”,”rb”))==NULL)
{
cout<<”\nCannot open file for reading”;
exit(2);
}
while(fread(&e,sizeof(Employee)1,f)
{
printf(“%s % s%f”, e.name, e.title, e.rate)
}
fclose(f);
}
Форматированный ввод-вывод
В некоторых случаях информацию удобно записывать в файл без преобразования,
т. е. в символьном виде пригодном для непосредственного отображения на экран. Для
этого можно использовать функции форматированного ввода-вывода:
1) int fprintf(FILE *f, const char*fmt,. . .) , где
FILE*f – указатель на файл, в который производится запись,
const char*fmt – форматная строка,
. . . – список переменных, которые записываются в файл.
Функция возвращает число записанных символов.
2) 1) int fscanf(FILE *f, const char*fmt, par1,par2, . . .) , где
FILE*f – указатель на файл, из которого производится чтение,
const char*fmt – форматная строка,
par1,par2,. . . – список переменных, в которые заносится информация из файла.
Функция возвращает число переменных, которым присвоено значение.
Пример:
void main()
{
FILE *f;
int n;
if((f=fopen(“int.dat”,”w”))==0)
{
perror(“int.dat”);
exit(0);
}
for(n=1;n<11;n++)
fprinf(f,”\n%d %d”,n,n*n);
fclose(f);
if((f=fopen(“int.dat”,”r”))==0)
{
perror(“int.dat”);
exit(1);
}
int nn;
while(fscanf(f, ”%d%d”,&n,&nn))
printf(“\n%d %d”,n,nn);
fclose(f);
}
Прямой доступ к файлам
Рассмотренные ранее средства обмена с файлами позволяют записывать и
считывать данные только последовательно. Операции чтения/записи всегда производятся,
начиная с текущей позиции в потоке. Начальная позиция устанавливается при открытии
потока и может соответствовать начальному или конечному байту потока в зависимости
от режима открытия файла. При открытии потока в режимах “r” и “w” указатель текущей
позиции устанавливается на начальный байт потока, при открытии в режиме “a” - за
последним байтом в конец файла. При выполнении каждой операции указатель
перемещается на новую текущую позицию в соответствии с числом
записанных/прочитанных байтов.
Средства прямого доступа дают возможность перемещать указатель
текущей позиции в потоке на нужный байт. Для этого используется функция
int fseek(FILE *f, long off, int org), где
FILE *f - – указатель на файл,
long off – позиция смещения
int org – начало отсчета.
Смещение задается выражение или переменной и может быть отрицательным, т. е.
возможно перемещение как в прямом, так и в обратном направлениях. Начало отсчета
задается одной из определенных в файле <stdio.h> констант:
SEEK_SET ==0 – начало файла;
SEEK_CUR==1 – текущая позиция;
SEEK_END ==2 – конец файла.
Функция возвращает 0, если перемещение в потоке выполнено успешно, иначе
возвращает ненулевое значение.
Примеры:
fseek(f,0L,SEEK_SET); //перемещение к началу потока из текущей позиции
fseek(f,0L,SEEK_END); //перемещение к концу потока из текущей позиции
fseek(f,-(long)sizeof(a),SEEK_SET); //перемещение назад на длину переменной а.
Кроме этой функции, для прямого доступа к файлу используются:
long tell(FILE *f);//получает значение указателя текущей позиции в потоке;
void rewind(FILE *f);//установить значение указателя на начало потока.
Удаление и добавление элементов в файле
Пример 1:
void del(char *filename)
{
//удаление записи с номером х
FILE *f, *temp;
f=fopen(filename,”rb”);//открыть исходный файл для чтения
temp=fopen(“temp”,”wb”)//открыть вспомогательный файл для записи
student a;
for(long i=0;.fread(&a,sizeof(student),1,f);i++)
if(i!=x)
{
fwrite(&a,sizeof(student)1,temp);
}
else
{
cout<<a<<" - is deleting...";
}
fclose(f); fclose(temp);
remove(filename);
rename(“temp”, filename);
}
Пример 2:
void add(char *filename)
{
//добавление в файл
student a;
int n;
f=fopen(filename,”ab”)открыть файл для добавления
cout<<"\nHow many records would you add to file?";
cin>>n;
for(int i=0;i<n;i++)
{
прочитать объект
fwrite(&a,sizeof(student),1,f);//записать в файл
}
fclose(f);//закрыть файл
}
3. ПРАКТИЧЕСКИЕ ЗАНЯТИЯ
Практическое занятие № 1
Тема: . Основные понятия
Цель: Ознакомить студентов, с основными понятиями, которые встречаются при
компиляции программ, а также рассмотреть понятия программных модулей,
препроцессора, системы программирования.
Краткие теоретические сведения по теме
1.1. Программные модули
Исходный модуль(source code) – это текст программы на языке программирования.
Объектный модуль (object code) – результат обработки компилятором исходного модуля.
Объектный модуль не может быть выполнен. Это незавершенный вариант машинной
программы. К объектному модулю в общем случае должны быть подсоединены модули
стандартной библиотеки, и он должен быть настроен по месту выполнения.
Исполняемый (абсолютный) модуль создает компоновщик (linker), объединяя в один
общий модуль объектные модули, реализующие отдельные части алгоритма. На этом
этапе к машинной программе подсоединяются необходимые функции стандартной
библиотеки.
Стандартная библиотека (library) – набор программных модулей, выполняющих
наиболее часто встречающихся в программировании задачи: ввод, вывод данных,
вычисление математических функций, сортировки, работа, с памятью и т.д. Модули
библиотеки хранятся в откомпилированном виде.
Ошибки, возникающие на этапе компиляции, - ошибки компиляции. Их разделяют на
синтаксические и семантические.
В период выполнения программы могут быть ошибки выполнения. Они появляются
либо из-за некорректной постановки задачи, либо из-за недопустимых данных и др.
Все программы, выполняемые на компьютере, составляют его программное
обеспечение. Работают программы благодаря его аппаратному обеспечению.
Распределение ресурсов компьютера при решении различных задач осуществляет
операционная система – комплекс программных модулей. Она контролирует работу
аппаратуры исполняемой программы и обеспечивает связь программиста с программой.
Несколько объединенных между собой компьютеров образуют компьютерную
сеть. Эти компьютеры могут поочередно использовать некоторые общие ресурсы, как
аппаратные, так и программные.
1.2. Препроцессор
До компиляции над программой обычно выполняются некоторые предварительные
действия: подключение текстов других исходных модулей, формирование
микроопределений, планирование условной компиляции и др. Эта работа выполняется так
называемым препроцессором, обычно являющимся составной частью компилятора.
Директивы препроцессора начинаются знаком # (на английском hash). Директива
может занимать несколько строк. В конце каждой строки, имеющей продолжение (т.е.
кроме последней), ставится обратная косая черта. Например,
#define text
Этот текст \
будет замещать \
слово text в программе.
Чаще всего препроцессор используется для того, чтобы подсоединить к
компилируемой программе файлы с текстами программных модулей пользователя и
соответствующих разделов системной библиотеки. Например,
#include<stdio.h>
#include<iostream.h>
#include<math.h>
#include<alloc.h>
Первые две дерективы обеспечивают подсоединение к программе разделов библиотек,
осуществляющих ввод/вывод данных, используемый в языке программирования Borland
C++; третья строка – раздел библиотеки с математическими функциями; последняя – с
функциями для работы с динамической памятью.
#include“progr.cpp”
Препроцессор данную директиву заменит текстом исходного модуля№ имеющего имя
progr.cpp.
Если имя задано в знаках < >, то поиск файла с этим именем выполняется в системных
каталогах, т.е. в системной библиотеке языка Borland C++.
Обычно в “ ” указывается имя файла программиста, текст которого необходимо включить
в программу, в С++ это имя можно записывать и в знаки < >. Имя записывается либо с
указанием полного пути к файлу, либо (как выше) поиск файла будет осуществляться
только в текущем каталоге.
1.3. Система программирования
Разработку, отладку и документирование программ осуществляют с помощью
программных комплексов, называемых системами программирования.
Основными компонентами системы программирования являются:
 Язык программирования;
 Интегрированная среда;
 Редактор связей (компоновщик, сборщик);
 Библиотеки различного назначения;
 Файлы документации.
Интегрированная среда включает в себя:
 Редактор текстов;
 Подсистему справочной информации;
 Подсистему работы с файлами;
 Подсистему управления компиляцией и редактированием связей;
 Отладчик программ.
Язык Borland C++ является языком среднего уровня. Он включает в себя элементы
машинно-ориентированных языков, т.е. имеется возможность работать с битами, байтами,
непосредственно обращаться к данным в оперативной памяти (векторам прерываний,
видеобуферу, буферу клавиатуры и т.д.). В отличие от языка ассемблера он намного
удобнее для написания прикладных и системных программ.
Практическое занятие № 2
Тема: Язык Borland C++. Элементы языка Borland C++
Цель: Ознакомить студентов с алфавитом, основными элементами данного языка
которые нужны для дальнейшего составления программ. Изучить правила составления
текстов программ.
Краткие теоретические сведения по теме
2.1. Алфавит языка
Множество символов языка Borland C++ можно разделить на четыре группы.
В первую группу входят буквы латинского алфавита и символ подчеркивания.
Строчные буквы используются для написания ключевых слов языка. Одинаковые
строчные и прописные буквы (например, а и А) имеют различные коды и при записи имен
переменных (идентификаторов) в языке Borland C++ различаются. Буквы русского
алфавита используются для вывода информации в текстах, комментариях.
Вторую группу используемых символов составляют цифры: 0,1,....,9.
В третью группу входят специальные символы. Большинство этих знаков
используется для разных целей. Специальные символы:
+ (плюс), - (минус), * (звездочка ), / (дробная черта), = (равно), > (больше), < (
меньше), ; (точка с запятой ), & (амперсанд ), [ ] (квадратные скобки), { } (фигурные
скобки), ( ) (круглые скобки), _ (знак подчеркивания), (пробел ), . (точка), , (запятая), :
(двоеточие), # (номер), % (процент), ~ (поразрядное отрицание), ? (знак вопроса), ! (
восклицательный знак), \ (обратный слэш), / (дробная черта), | (вертикальная черта), ‘
(апостроф), “ (кавычки).
2.2. Идентификаторы
Идентификатор – это имя, которым обозначается некоторый объект (данное) в
программе. Данные в оперативной памяти размещаются по некоторым адресам, заранее
неизвестным программисту. Для того чтобы в программе иметь возможность обращаться
к данным и обрабатывать их, программист этим данным дает условные имена, которые
компилятор в программе заменит адресами в оперативной памяти.
Для записи идентификаторов используются буквы латинского алфавита, цифры и
знаки подчеркивания. Идентификатор может начинаться с буквы или знака
подчеркивания. Компилятор различает идентификаторы по первым тридцати двум
символам.
Примеры записи идентификаторов: sum, result, n, m, c10, Beta,
beta, _function, letter, array и т.д.
Ошибочные идентификаторы: a+b, -omega, 9c, &b, %f, long, int,
if.
Так как строчные и прописные буквы различаются то идентификаторы BETA, beta,
Beta будут различными. При выборе идентификатора необходимо учитывать следующее:
1. Идентификатор не должен совпадать с ключевыми словами языка и именами функций
из библиотеки языка Borland C++;
2. Не рекомендуется начинать идентификатор со знака подчеркивания, так как этот
символ используется в именах некоторых библиотечных функций и при совпадении
имен эти функции будут недоступны программе.
2.3.
Ключевые слова
Ключевые слова – это имена, используемые в языке Borland C++ с некоторым
заранее определенным смыслом? Данные слова нельзя применять в качестве
идентификаторов объектов (данных) пользователя.
Ключевые слова сообщают
компилятору о типе данных, способе их организации, о последовательности выполнения
операторов.
К ключевым словам относятся: auto, break, case, catch, char, class, const, continue,
default, delete, do, double, else, enum, extern, float, friend, for, if, inline, int, long, new,
operator, private, protected, public, register, return, short, signed, sizeof, struct, switch,
template, throw, this, typedef, union, unsigned, void, volatile, while.
Ключевые слова near, far, huge определяют тип (размер) указателя на данные, а
слова _asm, cdecl, fortran, pascal используются для организации связи с функциями,
написанными на других языках программирования.
2.4 Типы данных
Следует различать тип данных и модификатор типа.
Существуют следующие базовые типы: char (символьный), int (целый), float
(вещественный), double (вещественный с двойной точностью), void (пустой тип).
К модификаторам относятся: unsigned (беззнаковый),. Signed (знаковый), short
(короткий),. Long (длинный).
Тип данных и модификатор типа определяют:
 Формат хранения данных в оперативной памяти (внутреннее представление данных);
 Диапазон значений, в пределах которого может изменяться переменная;
 Операции, которые могут выполняться над данными соответствующего типа.
Все типы данных можно разделить на две категории: скалярные и составные.
К скалярным типам данных относятся - символы, арифметические (целые,
вещественные), указатели, перечисления.
К
составным типам данных относятся – массив, структура, поля битов,
объединение.
2.4.1. Переменные
Данные, значения которых во время выполнения программы можно изменять,
называются
переменными,
неизменяемые данные называются константами. В
программе все данные перед их использованием должны быть объявлены или
определены. В операторах определения данных указываются тип данных и перечисляются
через запятую имена переменных, для каждой переменной в соответствии с типом
выделяется необходимое количество байтов памяти. Выделенному полю байтов
присваивается имя переменной, которое в дальнейшем используется в программе.
Идентификатор (имя переменной) может быть записан с квадратными скобками,
круглыми скобками или перед ним может быть один или несколько знаков *(звездочка).
Спецификатор типа – одно или несколько ключевых слов, определяющих тип
переменной. Язык Borland C++ определяет стандартный набор основных типов данных
(int, char, double), применяя которые пользователь может объявлять свои производные
(структурированные) типы (массив, структура, и др.).
Например:
int
j=10, m=3, n;
float
c=-1.3, l=-10.23, n;
Определения и объявления переменных рекомендуется размещать в начале
программного модуля.
Приведем размеры и возможные диапазоны базовых типов данных (таблица 1):
Таблица 1
Объем памяти, байт
Диапазон значений
Наименование типа
Тип данных
Символьный
char
1
-128…127
-32768…32767
2
Целый
int
Короткий
Short
2(1)
-32768…32767(-128…127)
Длинный
Long
4
-2147483648…2147483647
Беззнаковый целый
Unsigned int
0…65535
2
Беззнаковый длинный Unsigned long
4
0…424967295
Вещественный
Float
4
3,14*10-38…3,14*1038
Вещественный с
Double
8
1,7 *10-308 1,7 *10308
двойной точностью
Сложные типы данных подразделяются на массивы, структуры (struct), объединения или
смеси (union), перечисления (enum).
2.4.2. Константы
Константой называется данное, неизменяемое в процессе выполнения программы.
В языке Borland C++ используются следующие типы констант: целые, с плавающей
точкой, символьные, и строковые литералы.
Целая константа – это целое число, записанное в десятичной, шестнадцатеричной
или восьмеричной системе счисления.
Десятичная константа – любое целое десятичное число со знаком или без знака и
начинающееся со значащей цифры.
Восьмеричная константа – это целое число, записанное в восьмеричной системе
счисления и начинающееся с обязательного нуля.
Шестнадцатеричная константа начинается с обязательных знаков 0х или 0Х
(нуль, х) и является записью числа в шестнадцатеричной системе.
Примеры записи целых констант
Десятичная
Восьмеричная
Шестнадцатеричная
+15
+017
0хf
-71
-087
-0x47
379
0573
0x17B
Примеры записи символьных констант: ‘A’, ‘9’, ’+’, ‘%’, ‘-‘, ‘# ’.
Примеры записи строковых констант: “JAVA”, “ЭВМ”, “int”.
...{ const float f24=2.4, f13=1.3;... /*объявление констант вещественного
типа*/
Практическое занятие № 3
Тема: Структура простой программы
Цель: Ознакомить с правильной записью структуры простой программы данного языка.
Изучить правила составления текстов программ
Краткие теоретические сведения по теме
3.1. Комментарии
Комментарий – любая последовательность символов, начинающаяся парой
символов /* и заканчивающаяся парой символов */ или начинающаяся // и до конца
текущей строки.
3.2. Структура программы
Программа, написанная на языке Borland C++, состоит из директив препроцессора,
объявлений глобальных переменных, одной или нескольких функций, cреди которых одна
главная (main) функция управляет работой всей программы.
Общая структура программы на языке Borland C++ имеет вид:
<директивы препроцессора>
<определение типов пользователя – typedef>
<прототипы функций>
<определение глобальных объектов>
<функции>
Функции, в свою очередь, имеют структуру:
<класс_памяти> <тип> <имя функции> (<объявление параметров>)
{
- начало функции
<определение локальных объектов>
<операции и операторы>
}
- конец функции
Перед компиляцией программы на языке Borland C++ автоматически выполняется
предварительная (препроцессорная) обработка текста программы. С помощью директив
препроцессора задаются необходимые действия по преобразованию текста программы
перед компиляцией.
Директивы записываются по следующим правилам:
все препроцессорные директивы должны начинаться с символа #;
все директивы начинаются с первой позиции;
сразу за символом # должно следовать наименование директивы,
указывающее текущую операцию препроцессора.
Наиболее распространены директивы #include и #define.
Директива #include используется для подключения к программе заголовочных
файлов (обычных текстов) с декларацией стандартных библиотечных функций. При
заключении имени файла в угловые скобки < > поиск данного файла производится в
стандартной директории с этими файлами. Если же имя файла заключено в двойные
кавычки ” ”, то поиск данного файла осуществляется в текущем директории.
Например:
#include <stdio.h> - подключение файла с объявлением стандартных функций
файлового ввода-вывода;
#include <conio.h>
- функции работы с консолью;
#include <graphics.h> - графические функции;
#include <math.h>
- математические функции.
Директива #define (определить) создает макроконстанту и ее действие распространяется на весь файл.
Например:
#define PI 3.1415927
В ходе препроцессорной обработки программы идентификатор PI заменяется значением 3,1415927.
Пример программы:
#include <stdio.h>
#include < conio.h>
/* Директивы препроцессора */
#define PI 3.1415927
void main()
// Заголовок главной функции
{
// Начало функции
int num;
// Декларирование переменной num
num=13;
// Операция присваивания
clrscr();
// Очистка экрана
printf(«\n Число pi=%7.5f\n %d-это опасное число \n”, PI, num);
}
// Конец функции
6..
6..
6..
В первых двух строках программы указаны директивы препроцессора #include, по
которым происходит подключение заголовочных файлов, содержащих декларации
функций ввода-вывода (stdio.h) для функции printf() и работы с консолью (conio.h) для
функции clrscr(). Следующая директива создает макроконстанту PI и подставляет вместо
ее имени значение 3,1415927 по всему тексту программы. В главной функции main
декларируется переменная целого типа num. Далее этой переменной присваивается
значение 13. Функция printf выводит на экран строки:
Число pi =3.1415927
13 – это опасное число
Как видно, функция представляет собой набор операций и операторов, каждый из
которых оканчивается символом ; (точка с запятой).
В тексте программы использованы комментарии между парой символов /* */ и
после пары символов //.
Практическое занятие № 4
Тема. Функции ввода-вывода. Форматы преобразования данных
Цель: Изучить функции вывода, ввода данных, рассмотреть основные форматы
функции вывода, а также основные математические функции, операции присваивания
Краткие теоретические сведения по теме
4.1. Функции вывода информации
Для вывода информации в языке Borland C++ используются следующие функции:
Функция putchar() обеспечивает вывод одиночного символа без перехода на новую
строку.
Функция puts() используется для вывода строки символов с переходом на начало
новой строки.
Функция printf() предназначена для форматированного вывода данных. Ее
формат:
рrintf (<управляющая строка>, <список аргументов>);
Управляющая строка заключается в кавычки и указывает компилятору вид
выводимой информации. Она может включать спецификации преобразования и
управляющие символы.
Спецификация преобразования имеет вид:
% <флаг> <размер поля . точность> спецификация
где флаг может принимать следующие значения:
- выравнивание влево выводимого числа (по умолчанию выполняется
выравнивание вправо);
+ выводится знак положительного числа;
размер поля – задает минимальную ширину поля, т.е. длину числа. При
недостаточной ширине поля выполняется автоматическое расширение;
точность – задает точность числа, т. Е. количество цифр в его дробной части;
спецификация указывает вид выводимой информации. Ниже приведены основные
форматы функции печати:
Таблица 2
Формат
Тип выводимой информации
%i
Целое число
%d
Десятичное целое число
%c
Один символ
%s
Строка символов
%e
Число с плавающей точкой (экспоненциальная запись)
%f
Число с плавающей точкой (десятичная запись)
%u
Десятичное число без знака
%o
Восьмеричное число без знака
%x
Шестнадцатеричное число без знака
Для длинных чисел (long, double) – используется дополнительный формат l.
Например: %ld – длинное целое, %lf – вещественное число с удвоенной
точностью.
При необходимости вывода символа % его нужно указать 2 раза.
Например: printf(«Только %d%% предприятий не работало.\n»,5);
Получим:
Только 5% предприятий не работало.
Управляющая строка может содержать следующие управляющие символы:
\n – переход на новую строку;
\t – горизонтальная табуляция;
\v – вертикальная табуляция;
\b – сдвиг текущей позиции влево;
\r – возврат в начало строки;
\f – прогон бумаги до начала новой страницы;
\a – звуковой сигнал;
\ddd – 8-ричный ASCII-код;
\xhhh – 16-ричный- -код;
\? – знак вопроса.
Список аргументов – печатаемые объекты (константы, переменные или
выражения), вычисляемые перед выводом. Количество аргументов и их типы должны
соответствовать спецификациям преобразования в управляющей строке.
Пример:
#include <stdio.h>
#define PI 3.1415926
main()
{
int number=5, cost=11000, s=-777;
float bat=255, x=12.345;
printf(«%d студентов съели %f бутербродов.\n», number, bat);
printf(«Значение числа pi равно%f.\n», PI);
printf(«Любовь и голод правят миром.\n»);
printf(«Стоимость этой вещи %d%s.\n», cost,»Руб.»);
printf («x=%-8.4f s=%5d%8.2f», x, s, x);
}
В результате выполнения последней функции printf () на экране будет выведено:
х=12.3450 s= -777 12.34
4.2. Функции ввода информации
Функция getch () используется для ввода одиночных символов.
Функия gets () обеспечивает ввод строки символов до нажатия клавиши ENTER.
Функция scanf предназначена для форматированного ввода информации любого вида.
Общий вид функции:
scanf (<управляющая строка>, < список адресов>);
Для нее, как и для функции printf (), указывается управляющая строка. Однако
функция scanf(), в отличие от функции printf (), использует в списке адресов указатели на
переменные, т.е. их адреса. Для обозначения указателя перед именем переменной
записывается символ &, обозначающий адрес переменной. Для ввода значений строковых
переменных символ & не используется. При использовании формата %s строка вводится
до первого пробела. Вводить данные можно как в одной строке через пробел, так и в
разных строках.
Данную особенность иллюстрирует следующий участок программы:
...
int course;
float grant;
char name[20];
printf( «Укажите ваш курс, стипендию, имя \n»);
scanf( «%d%f», &course, &grant);
scanf( «%s», name);
/* & отсутствует при указании массива символов */
...
Язык Borland C++ предусматривает альтернативную функциям printf() и scanf()
возможность обработки ввода/вывода стандартных типов данных и строк.
Простой пример:
printf(“Введите число”); scanf(“%d”, &n);
В Borland C++ записывается так:
cout <<”введите число”; cin>>n;
В первом операторе используются стандартный выходной поток cout и операция
<< (операция передачи в поток). Во втором операторе используются стандартный входной
поток cin и операция >> (операция извлечения из потока). Эти операции не требуют
форматирующих строк и спецификаторов преобразования для указания на тип входных и
выходных данных, а также операции взятия адреса & переменной n.
Для организации потокового ввода/вывода программы на Borland C++ должны
содержать заголовочноый файл iostream.h.
4.3. Стандартные математические функции
Декларации математических функций языка Borland C++ содержатся в файле
<math.h>. В последующих записях аргументы x и y имеют тип double, параметр n имеет
тип int. Аргументы тригонометрических функций задаются в радианах (2π радиан = 360).
Все приведенные математические функции возвращают значение (результат) типа double.
Таблица 3
Математическая функция
Имя функции в языке Borland C++
sqrt(x)
x
|x|
fabs(x)
ex
exp(x)
y
x
pow(x,y)
ln(x)
log(x)
lg10(x)
log10(x)
sin(x)
sin(x)
cos(x)
cos(x)
tg(x)
tan(x)
arcsin(x)
asin(x)
arccos(x)
acos(x)
arctg(x)
atan(x)
arctg(x/y)
atan2(x,y)
x -x
sh(x)=1/2 (e -e )
sinh(x)
ch(x)=1/2 (ex+e-x)
cosh(x)
tgh(x)
tanh(x)
Остаток от деления x на y
fmod(x,y)
Наименьшее целое, которое >=x
ceil(x)
Наибольшее целое, которое <=x
floor(x)
4.4. Операция присваивания
Операция присваивания имеет две формы записи:
1. Полная форма:
имя_переменной =выражение;
Сначала
вычисляется
выражение,
а
затем
результат
присваивается
имени_переменной. Например:
y =(x+2)/(3*x)-5;
С помощью одного оператора можнo присвоить одно значение нескольким переменным, например: x=y=z=0;
/* x, y, z=0 */
или
z=(x=y)*5;
- сначала переменной x присваивается значение
переменной y, далее вычисляется выражение x*5 , и результат присваивается переменной
z.
2. Сокращенная форма:
имя_переменной операция=выражениe;
где операция – одна из арифметических операций (+ , -, *, /, %);
Например: x*=5;
/* x=x*5;
*/
s+=7;
/* s=s+7;
*/
y/=x+3;
/* y=y/(x+3); */
Сокращенная форма операции присваивания применяется тогда, когда переменная
используется в обеих частях полной формы данного оператора.
В языке Borland C++ существует операции уменьшения (--) и увеличения (++)
значения переменной на 1. Операции могут быть префиксные (++i и –i) и
постфиксные (i++ и i--). При использовании данной операции в выражении, в
случае префиксной операции сначала выполняется сама операция (изменяется значение i),
и только потом вычисляется выражение. В случае постфиксной операции – операция
применяется после вычисления выражения.
Например: b=7;
n=1;
1. c=b*++n;
/* n=n+1; c=b*n; т.е. c=14 */
2. c=b*n++;
/* c=b*n; n=n+1; т.е. c=7 */
Практическое занятие № 5
Тема. Понятие пустого и составного операторов. Операторы перехода
Цель: Ознакомиться с составным, пустым операторами, с операторами разветвлений
(оператор выбора по условию if, оператор-переключатель switch). Научиться
программировать линейные и разветвляющиеся алгоритмы.
Краткие теоретические сведения по теме
Составной оператор – это несколько операторов, собранных в блок с помощью
фигурных скобок “{ }” или разделенных запятой. Такой блок можно рассматривать как
один оператор.
Пустой оператор – это просто знак “;”. Им можно заменить оператор там, где не
нужно выполнять никакого действия.
5.1. Оператор разветвления (if)
Операторы разветвления - выбирают в программе среди нескольких вариантов ее
возможного продолжения единственный вариант вычислительного процесса.
Оператор If имеет следующую общую форму записи:
if (условие) оператор1;
[else оператор2;]
при выполнении оператора if сначала вычисляется условие.
Если результат – истина (любое отличное от нуля значение), то выполняется оператор 1.
Если результат анализа условия – ложь (равен 0), то выполняется оператор 2. Если слово
else отсутствует, то оператор 1 пропускается, а управление передается на следующий
после if оператор.
В качестве условия может использоваться арифметическое, логическое выражение,
выражение сравнения, целое число, переменная целого типа, вызов функции с
соответствующим типом возвращаемого значения.
Например:
...
if(x=y) printf(“\n числа равны”);
...
5.2. Конструкция if – else
В некоторых ситуациях необходимо указать не только оператор (блок операторов),
выполняемый в случае истинности условия, но и оператор (или блок), выполняемый при
ложности условия.
If (условие) оператор1;
else оператор2;
Свойства такие же, как у предыдущей конструкции.
Например:
...
if(x>y) max=x;
else max=y;
...
5.3. Вложенные else – if
При помощи if и else можно также составлять else – if – конструкции, которые могут
осуществлять проверку нескольких выражений.
If (условие) operator1;
else if (условие) operator2;
else if (условие) operator3;
else operator4;
/* Пример: ввести число и определить, больше ли оно нуля, меньше или равно нулю */
#include<stdio.h>
#include<conio.h>
void main()
{
int n;
printf(“\n введите n”);
scanf(“%i”,&n);
if (n>0) printf(“\n число больше нуля”);
if (n<0) printf(“\n число меньше нуля”);
else printf(“\n число равно нулю”);
}
Свойства конструкции:
 Условия проверяются в том порядке, в котором они перечислены в программе;
 Если одно из условий истинно, то выполняется оператор, соответствующий этому
условию, а проверка оставшихся условий не производится;
 Если ни одно из проверенных условий не дало истинного результата, то выполняются
операторы, относящиеся к последнему else;
 Последний else является необязательным, следовательно, он и относящийся к нему
оператор могут отсутствовать.
Триадный оператор (оператор условного перехода ?). Его форма:
имя_переменной =условие ? выражение_1 : выражение_2;
Если условие истинно, то имени_переменной присваивается результат
выражения_1, иначе – выражения_2.
Например: найти наибольшее из двух чисел:
max=a>b ? a : b;
5.4. Оператор выбора switch
В программировании часто встречается задача выбора одного варианта решения
задачи из многих существующих. Это можно сделать с помощью вложенных if...else.
Однако более удобный способ – использование оператора выбора switch, общий формат
которого следующий:
switch(выражение)
{
case consnant1: оператор1; break;
...
case consnantN: операторN; break;
default: оператор; break;
}
где consnant1…consnantN – целые или символьные константы; default – выполняется, если
результат выражения не совпал ни с одной константой; может отсутствовать; break –
oператор завершения работы switch. После выполнения одной из ветвей case все
остальные ветви будут опущены. Если оператор break не записан, то выполняются
операторы следующей ветви case. Оператор switch проверяет, совпадает ли значение
выражения с одним из значений, приведенных ниже констант. При совпадении
выполняются операторы, стоящие после совпавшей константы.
Например:
...
switсh (i)
{
case 1: f=pow(x,2); break;
case 2: f=fabs(x); break;
case 3: f=sqrt(x); break;
default: printf(“Ошибка!”);exit(1);
}
f=f+5;
...
Практическое занятие № 6
Тема. Операторы цикла
Цель: Изучить циклические операторы for, while, do – while, научиться составлять
и программировать циклические алгоритмы.
Краткие теоретические сведения по теме
В большинстве задач, встречающихся на практике, вычисления по некоторой группе
формул необходимо выполнять многократно. Этот многократно повторяющийся участок
вычислительного процесса называют циклом. Т.о., под циклом понимается оператор или
группа операторов, повторяющихся некоторое количество раз. Каждый проход по телу
цикла называется итерацией.
6.1. Оператор while
Основная форма циклического оператора while:
while (<условие>) <оператор>;
где оператор – это простой, составной или пустой оператор.
Цикл выполняется до тех пор, пока условие принимает значение «истина», т.е.
выражение в скобках возвращает ненулевой результат. Это цикл с предусловием – сначала
проверяется условие, затем выполняется оператор. Поэтому цикл while не выполнится ни
разу, если изначально результат вычисления условия будет равен 0.
6.2. Оператор do...while
Основная форма оператора do – while:
do <оператор> while (<условие>);
или
do
{
<оператор_1>; <оператор_2>...<оператор_n>;
}
while (<условие>);
где оператор – это простой, составной или пустой оператор.
Оператор do...while – оператор цикла с постусловием, т.е. сначала выполняется
оператор, а затем проверяется условие на истинность. Так как в цикле do...while условие
проверяется в конце цикла, то цикл будет выполнен хотя бы один раз.
В циклах типа while и do...while допустимы те же способы досрочного выхода из
цикла и досрочное завершение текущего шага цикла, как и в операторе for, но в
последнем случае в отличие от цикла for управление передается на проверку условия. Для
предотвращения бесконечного цикла, внутри циклов while и do–while нужно
предусмотреть изменение переменных, входящих в условие.
6.3. Оператор for
При организации цикла, когда его тело должно быть выполнено фиксированное
количество раз, осуществляются три операции: инициализация счетчика, сравнение его
величины с некоторым граничным значением и изменение значения счетчика при каждом
прохождении тела цикла.
Основная форма оператора цикла for имеет вид:
for (выражение_1; выражение_2; выражение_3 ) <оператор>;
где
выражение_1 – инициализация начального значения параметра цикла;
выражение_2 – проверка условия на продолжение цикла;
выражение_3 – изменение параметра цикла (коррекция параметра);
оператор – простой или составной оператор языка Borland С++.
Схема работы оператора следующая: только один раз вначале вычисляется
выражение_1, затем проверяется выражение_2, и если оно - «истина», то выполняется
циклический участок программы, затем производится коррекция параметра, и так до тех
пор, пока выражение_2 не примет значение «ложь».
Например:
for (k=1; k<5; k++)
printf(“\n %d”, k);
В результате выполнения этого оператора печатаются в столбик цифры от 1 до 4.
В качестве параметра цикла можно использовать переменную любого базового типа.
Например:
for(ch=’a’; ch<=’z’; ch++) /* вывод на экран БУКВ */
printf(“ %c”,ch);
/* латинского алфавита */
Необходимо тщательно контролировать структуру циклов for в программе, чтобы не
получился бесконечный цикл (из которого нет выхода).
Например:
for(k=10; k>6;k++)
printf(“бесконечный цикл\n”);
Выйти из цикла досрочно можно следующими способами:
1. по дополнительному условию;
2. используя операторы:
 break;
- завершения работы цикла, в котором находится break, управление
передается на первый после цикла выполняемый оператор;
 exit(int Kod); - происходит выход из программы;
 return;
- осуществляется выход из функции;
- с помощью оператора безусловного перехода goto <метка>;
Досрочное завершение текущего циклического шага возможно при помощи
дополнительного условия или оператора continue, который прерывает выполнение
текущего шага цикла, т.е. пропускает операторы оставшейся части цикла и передает
управление в головной оператор цикла для коррекции параметра и проверки условия.
Передавать управление извне вовнутрь цикла запрещается.
Любое из выражений цикла for в круглых скобках может отсутствовать, но символ
«;» опускать нельзя.
Например:
int i=0;
for(; i<3; i++)
puts(“Hello!”);
6.4. Вложенные циклы
В случае вложенных циклов один цикл находится внутри другого, например:
for(i=nn;i<nk;i++)
for(j=mn;j<mk;j++)
оператор;
где оператор – это простой, составной или пустой оператор. Внутренний цикл будет
выполняться для каждого значения параметра i, удовлетворяющего условию внешнего
цикла.
Пример:
int i,j;
for(i=1;i<10;i++) /* ПЕЧАТЬ ТАБЛИЦЫ УМНОЖЕНИЯ */
{
/* ЦЕЛЫХ ЧИСЕЛ */
for(j=1;j<4;j++)
printf(“\n %d*%d=%2d”, i, j, i*j);
printf(“\n”);
}
Пример 1:
N
1
. На печать программа должна выводить промежуточные и
k 1 k
окончательный результаты.
Текст программы может иметь вид:
Вычислить S  
#include <stdio.h>
#include <conio.h>
void main(void)
{
float s;
int k,N;
puts(“Введите N”);
scanf(“%d”,&N);
for (s=0,k=1; k<=N; k++)
{
s+=1.0/k;
printf(« \n k=%d s=%f «, k, s);
}
printf(«\n ОТВЕТ: s=%f, Press any key...»,s);
getch( );
}
Практическое занятие № 7
Тема. Массивы
Цель: Изучить правила работы с одномерными массивами, а также особенности
работы со строковыми объектами, как одномерными символьными массивами.
Краткие теоретические сведения по теме
7.1. Одномерные массивы
Массив – конечномерная последовательность данных одного типа. Массив –
объект сложного типа. Каждый элемент массива определяется именем массива и индексом
(целое число), по которому к элементу массива производится доступ. Рассмотрим
одномерные массивы. Индексы у массивов в языке Borland C++ начинаются с 0. В
программе одномерный массив объявляется следующим образом:
<Тип> <имя массива>[размер];
где, размер – количество элементов одномерного массива.
Размер массива может задаваться константой или константным выражением.
Нельзя задавать массив переменного размера, для этого существует отдельный механизм –
динамическое выделение памяти.
Пример объявления массива целого типа:
int a[5];
В массиве а первый элемент а[0], второй – а[1], …, пятый - а[4]. В языке Borland С++ не
проверяется выход индекса за пределы массива. Корректность использования индексов
элементов массива должен контролировать программист.
Пример работы с одномерным массивом
В массиве x целого типа найти индекс и значение максимального элемента.
Текст программы может быть следующим:
#include <stdio.h>
#include <conio.h>
void main(void)
{
int x[10];
// индексы принимают значения от 0 до 9
int max, mmax, i ;
puts("\n введите 10 чисел, по одному в строке \n");
for (i=0; i<10; i++)
{ scanf(“%i”,&x[i]);}
max=x[0]; nmax=0;
for (i=1; i<10; i++)
if (x[i]>max)
{ max=x[i]; nmax=i;
}
printf(" \n
max = %5i, nmax=%3i”,max,nmax);
getch();
}
7.2. Многомерные массивы
Кроме одномерных массивов возможна работа с многомерными массивами.
Объявление многомерного массива:
<тип><имя>[<размер 1>][<размер 2>]…[<размер N>]={{список начальных
значений}, {список начальных значений},…};
Наиболее быстро изменяется последний индекс элементов массива, поскольку
многомерные массивы размещаются в памяти компьютера в последовательности
столбцов.
Например, элементы двухмерного массива b[2][1] размещаются в памяти в
следующем порядке:
b[0][0], b[0][1], b[1][0], b[1][1], b[2][0], b[2][1].
Следующий пример иллюстрирует определение массива целого типа, состоящего из трех
строк и четырех столбцов, с инициализацией начальных значений:
int a[3][4] = {{0,1,2,0},{9,-2,0,0},{-7,1,6,8}};
Если в какой-то группе {…} отсутствует значение, то соответствующему элементу
присваивается 0. Предыдущий оператор будет эквивалентен следующему определению:
int a[3][4] = {{0,1,2},{9,-2},{-7,1,6,8}};
Пример программы
Создать двумерный массив целых чисел NxM (N и M не более 50), используя
функцию rand и вывести на экран в форме матрицы, N,M ввести с клавиатуры:
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#define rnd (rand()/32768.0) // rand - генератор случайных чисел от 0 до int,
rnd – от 0 до 1
void main(void)
{ int i, j, n, m, a[50][50];
puts(“\n Input n, m:”);
scanf(“%d %d”,&n,&m);
printf(“\n Array a \n”);
for(i=0; i<n; i++)
for(j=0; j<m; j++)
{
a[i][j]=rnd*10-5;
// диапазон от –5 до 5
printf(“%d%c“, a[i][j], (j= =m-1)?’\n’:’ ‘);
}
getch();
}
7.3. Строки
В алгоритмическом языке PASCAL существует отдельный тип данных – строка,
который объявляется с атрибутом string. В языке Borland C++ отдельного типа данных
«строки символов» нет. Работа со строками реализована путем использования
одномерных массивов типа char, т.е. строка символов – это одномерный массив типа
char, заканчивающийся нулевым байтом. Нулевой байт – это байт, каждый бит которого
равен нулю, при этом для нулевого байта определена символьная константа ´\0´ (признак
окончания строки или нуль-терминатор). Поэтому, если строка должна содержать k
символов, то в описании массива необходимо указать k+1 элемент.
Например, описание: char a[7], означает, что строка содержит шесть символов, а
последний байт отведен под нулевой.
Строковая константа в языке С – это набор символов, заключенных в двойные
кавычки. Например: “Лабораторная работа по строкам”. В конце строковой константы
явно указывать символ ´ \0 ´ не нужно, так как это сделает компилятор языка С.
Строки можно инициализировать при декларировании, например:
char S1[10]=”123456789”, S2[]=”12345”;
в последнем случае размер строки будет установлен по количеству символов.
Для ввода строки с клавиатуры дисплея используются две стандартные библиотечные функции, прототипы которых приведены в файле stdio.h.
Функция scanf( ) вводит значения для строковых переменных спецификатором
ввода %S. Но надо помнить, что функция scanf( ) вводит символы до появления первого
символа “пробел”.
Библиотечная функция gets( ), обеспечивает ввод строки с пробелами внутри этой
строки. При этом ввод строки символов завершается нажатием клавиши ENTER.
Обе функции автоматически ставят в конец строки нулевой байт. И, кроме того,
так как строка – это символьный массив, а имя массива – это указатель на его начало в
памяти, то символ «&» перед именами строковых объектов при использовании этих
функций указывать не надо.
Вывод строк производится функциями printf( ) или puts( ). Обе функции выводят
символьный массив до первого нулевого байта. Функция printf( ) не переводит курсор
после вывода на начало новой строки, программист должен предусмотреть такой перевод
в строке формата. Функция puts( ) автоматически переводит курсор после вывода
строковой информации в начало новой строки.
Остальные операции над строками выполняются с использованием стандартных
функций. Декларации функций для работы со строками размещены в файле string.h. Вот
некоторые из наиболее часто используемых:
1. Функция strcpy(S1, S2) - копирует содержимое строки S2 в строку S1.
2. Функция strcat(S1, S2) - присоединяет строку S2 к строке S1 и помещает ее в массив,
где находилась строка S1, при этом строка S2 не изменяется. Нулевой байт, который
завершал строку S1, заменяется первым символом строки S2.
3.Функция strcmp(S1, S2) сравнивает строки S1 и S2 и возвращает значение =0, если
строки равны, т.е. содержит одно и то же число одинаковых символов; значение <0, если
S1<S2;значение >0, если S1>S2.
4. Функция strlen(S) возвращает длину строки, при этом завершающий нулевой байт не
учитывается.
Пример работы со строковыми данными
В программе значение строки вводится с клавиатуры, затем введенная строка
распечатывается в обратном порядке.
#include <stdio.h>
#include <string.h>
#include <conio.h>
void main(void)
{
char s[100]; // объявление символьного массива
int i, k;
puts(" Введите исходную строку");
gets(s);
k=strlen(s);
puts(" РЕЗУЛЬТАТЫ РАБОТЫ ПРОГРАММЫ \n");
for (i=k; i>=0; i--)
printf("%c",s[i]); /* вывод элементов массива в обратном порядке */
printf("\n Press any key...");
getch();
}
Практическое занятие № 8
Тема. Указатели
Цель: Ознакомиться с основными понятиями, такими как указатели, рассмотреть
операции над указателями, указатели на указатели, массивы указателей
Краткие теоретические сведения по теме
8.1. Указатели и операции над адресами
Обращение к объектам любого типа как операндам операций в языке Borland C++
может проводиться:
- по имени, как мы до сих пор делали;
- по указателю (косвенная адресация).
Указатель – это переменная, которая может содержать адрес некоторого объекта
в памяти компьютера, например адрес другой переменной. И через указатель,
установленный на переменную можно обращаться к участку оперативной памяти,
отведенной компилятором под ее значения.
Указатель объявляется следующим образом:
тип *идентификатор;
Например: int *a, *d;
float *f;
Здесь объявлены указатели a, d, которые можно инициализировать адресами
целочисленных переменных и указатель f, который можно инициализировать адресами
вещественных переменных.
С указателями связаны две унарные операции: & и *. Операция & означает «взять
адрес» операнда (т.е. установить указатель на операнд). Данная операция допустима
только над переменными. Операция * имеет смысл: «значение, расположенное по
указанному адресу» и работает следующим образом:
- Определяется местоположение в оперативной памяти переменной типа указатель.
- Извлекается информация из этого участка памяти и трактуется как адрес переменной
с типом, указанным в объявлении указателя.
- Производится обращение к участку памяти по выделенному адресу для проведения
некоторых действий.
Пример 1:
int x,
/* переменная типа int */
*y;
/* указатель на элемент данных типа int */
y=&x;
/* y - адрес переменной x */
*y=1;
/* косвенная адресация указателем поля x
/* “по указанному адресу записать 1”, т.е. x=1; */
Пример 2:
int i, j=8, k=5, *y;
y=&i;
*y=2;
/* i=2 */
y=&j;
/* переустановили указатель на переменную j */
*y+=i;
/* j+=i , т.е. j=j+1 -> j=j+2=10 */
y=&k;
/*переустановили указатель на переменную k */
k+=*y;
/* k+=k, k=k+k = 10 */
(*y)++;
/* k++, k=k+1 = 10+1 = 11 */
Говорят, что использование указателя означает отказ от именования
(разыменование) адресуемого им объекта.
Отказ от именования объектов при наличии возможности доступа по указателю
приближает язык Borland C++ по гибкости отображения «объект-память» к языку
ассемблера.
При вычислении адресов объектов следует учитывать, что идентификатор массива
и функции именует переместимую адресную константу (термин ассемблера), или
константу типа указатель (термин языка Borland C++). Такую константу можно присвоить
переменной типа указатель, но нельзя подвергать преобразованиям:
int x[100], *y;
y=x;
// присваивание константы переменной
...
x=y;
// Ошибка: в левой части - указатель-константа
Указателю-переменной можно присвоить значение другого указателя либо
выражения типа указатель с использованием, при необходимости операции приведения
типа (приведение необязательно, если один из указателей имеет тип "void *").
int i, *x;
char *y;
x=&i;
/* x -> поле объекта int */
y=(char *)x;
/* y -> поле объекта char */
y=(char *)&i; /* y -> поле объекта char */
Рассмотрим фрагмент программы:
int a=5, *p, *p1, *p2;
p=&a; p2=p1=p;
++p1; p2+=2;
printf(“a=%d,p=%d,p=%p,p1=%p, p2=%p.\n”,a,p,p,p1,p2);
Результат выполнения:
a=5, *p=5, p=FFC8, p1=FFCC, p2=FFD0.
Конкретные значения адресов зависят от ряда причин: архитектура компьютера,
тип и размер оперативной памяти и т.д.
8.2. Операции над указателями (адресная арифметика)
Указатель может использоваться в выражениях вида:
p # ie, ##p, p##, p# = ie,
где p - указатель, ie - целочисленное выражение, # - символ операции '+' или '-'.
Значением таких выражений является увеличенное или уменьшенное значение
указателя на величину ie*sizeof(*p). Следует помнить, что операции с указателями
выполняются в единицах памяти того типа объекта, на который ссылается этот указатель.
Текущее значение указателя всегда ссылается на позицию некоторого объекта в
памяти с учетом правил выравнивания для соответствующего типа данных. Таким
образом, значение p#ie указывает на объект того же типа, расположенный в памяти со
смещением на ie*sizeof(*p) позиций.
Разрешается сравнивать указатели и вычислять разность двух указателей. При
сравнении могут проверяться отношения любого вида (">",">=","<","<=","==","!=").
Наиболее важными видами проверок являются отношения равенства или неравенства.
Отношения порядка имеют смысл только для указателей на последовательно
размещенные объекты (элементы одного массива).
Разность двух указателей дает число объектов адресуемого ими типа в
соответствующем диапазоне адресов. Очевидно, что уменьшаемый и вычитаемый
указатель также должны соответствовать одному массиву, иначе результат операции не
имеет практической ценности.
Любой указатель можно сравнивать со значением NULL, которое означает
недействительный адрес. Значение NULL можно присваивать указателю как признак
пустого указателя. NULL заменяется препроцессором на выражение (void *)0.
8.3. Указатели на указатели
В языке Borland C++ можно описать переменную типа «указатель на указатель».
Это ячейка оперативной памяти, в которой будет храниться адрес указателя на какую либо
переменную. Признак такого типа данных – повторение символа «*» перед
идентификатором переменной. Количество символов «*» определяет уровень
вложенности указателей друг в друга. При объявлении указателей на указатели возможна
их одновременная инициализация.
Например:
int a=5;
int *p1=&a;
int **pp1=&p1;
int ***ppp1=&pp1;
Теперь присвоим целочисленной переменной а новое значение, например 10.
Одинаковое присваивание произведут следующие операции:
a=10;
*p1=10;
**pp1=10; ***ppp1=10;
Для доступа к области ОП, отведенной под переменную а можно использовать и
индексы. Справедливы следующие аналоги:
*p1 <-> p1[0] **pp1 <-> pp1[0][0]
***ppp1 <-> ppp1[0][0][0]
Таким образом, указатели на указатели – это имена многомерных
массивов.
Соответствие между указателями и массивами с произвольным числом измерений
на примере четырехмерного массива:
float name[][][][];
<-> float
****name;
В последнем случае эквивалентными являются выражения:
name[i][j][k][l]
*(*(*(*(name+i)+j)+k)+l)
*(*(*(name+i)+j)+k)[l]
*(*(name+i)+j)[k][l]
*(name+i)[j][k][l]
8.4. Массивы указателей
В языке Borland C++ можно использовать массивы указателей, элементы которых
содержат, как правило, указатели на строковые данные. Объявляется такой массив,
например, так: char *m[5]. Здесь массив m[5] – массив, который может содержать пять
адресов данных типа char.
Массив указателей можно при объявлении инициализировать, т.е. назначать при
объявлении его элементам конкретные адреса заданных строк.
В качестве примера приведена программа, формирующая массив указателей с
одновременной инициализацией его элементов. Программа распечатывает номер строки,
ее адрес и значение.
#include <stdio.h>
#include <conio.h>
void main(void)
{
int i,k;
char *m[]={"Winter", "Spring", "Summer", "Autoumn"};
k=sizeof(m)/sizeof(*m);
printf("\n size of array=%d",k);
for(i=0; i<k; i++)
printf("\n string - %d; adress - %p; string:%s" ,i,m[i],m[i]);
getch();
}
В результате получим:
size of array=4
string - 0; adress - 0042007C; string: Winter
string - 1; adress - 00420074; string: Spring
string - 2; adress - 0042006C; string: Summer
string - 3; adress - 00420064; string: Autoumn
Конкретные значения адресов зависят от ряда причин: архитектура компьютера, тип и
размер оперативной памяти и т.д
Практическое занятие № 9
Тема. Функции
Цель: Познакомиться с механизмом составления и организации взаимодействия
пользовательских функций языка Borland С++.
Краткие теоретические сведения по теме
9.1. Функции пользователя
В алгоритмическом языке Borland С++ кроме использования стандартных
функций существует возможность работать с функциями пользователя. Предварительно
функцию необходимо объявить. Объявление функции пользователя т.е. ее декларация
возможна в двух формах – в форме описания и в форме определения (реализации).
Описание функции – декларация ее прототипа вначале программного файла.
Используется следующий способ декларации функций:
<тип_результата> <имя_функции>(<тип> <переменная>, …<тип> <переменная>);
Идентификаторы переменных в круглых скобках прототипа указывать не
обязательно, так как компилятор языка их не обрабатывает.
Пример описания функции fun со списком параметров:
float fun(int, float, int, int);
Прототип функции сообщает компилятору о том, что далее в тексте программы
будет приведено полное определение (полный ее текст).
Полное определение функции имеет следующий вид:
<тип_результата> <имя_функции>(список параметров)
{
код функции
}
Тип результата определяет тип значения, который возвращается функцией в точку
ее вызова при помощи оператора возврата return. Если тип функции не указан, то по
умолчанию предполагается тип int. Список параметров состоит из перечня типов и имен
параметров, разделенных запятыми. Функция может не иметь параметров, но круглые
скобки необходимы в любом случае.
Оператор return вызывает немедленный выход из данной функции и возврат в
вызывающую ее функцию. Этот оператор также используется для возврата результата
работы функции. Отметим, что в теле функции может быть несколько операторов return,
но может и не быть ни одного. В таких случаях возврат в вызывающую ее функцию
происходит после выполнения последнего оператора.
Пример функции, определяющей наименьшее из двух целых чисел:
int mini(int x, int y)
{ int t;
if (x<y) t=x;
else t=y;
return t;
}
Можно написать функцию mini и таким образом:
mini(int x, int y)
{
return (x<y)? x:y; }
Если тип возвращаемого результата опущен, то он по умолчанию будет иметь тип int.
Все функции, возвращающие значение, должны использоваться в правой части
выражений языка Borland С++, иначе возвращаемый результат будет утерян. Но они не
могут использоваться в левой части операторов присваивания, за исключением тех
случаев, когда возвращается адрес результата работы функции.
Если функция не возвращает никакого значения, она должна быть описана как
функция типа void (пустая).
Например, для вывода горизонтальной строки на экран дисплея можно
использовать следующую функцию:
void lin(char a)
{
int k;
for(k=0; k<80; k++)
printf(“%c”, a);
}
Если у функции отсутствует список параметров, то при декларации такой
функции желательно в круглых скобках также указать ключевое слово void. Например,
заголовок основной функции должен выглядеть так: void main(void).
В языке Borland С++ каждая функция – это отдельный блок программы, вход в
который возможен только через вызов данной функции. Например, нельзя оператором
перехода goto передать управление внутрь любой функции.
Вызов функции имеет следующий формат:
<имя_функции> (список_аргументов)
где в качестве аргументов можно использовать константы, переменные,
выражения (их значения перед вызовом функции будут компилятором определены).
Аргументы списка вызова должны полностью совпадать со списком параметров
вызываемой функции по количеству, по порядку следования и по типам соответствующих
им параметров. Отметим, что в случае отсутствия аргументов (в заголовке функции
отсутствуют параметры) наличие круглых скобок у имени функции в точке ее вызова
обязательно.
Пример работы с функциями
Ввести массив NxN (не больше 50) целых чисел и в функции посчитать сумму его
положительных значений.
#include <stdio.h>
#include <conio.h>
void summa(int, int a1[ ][50]);
void main(void)
{
int a[50][50];
int i,j,N;
puts("\n Введите размер массива N(<50)\n");
scanf(“%d”,&N);
printf("\n Введите данные \n");
for(i=0; i<N; i++)
for(j=0; j<N; j++)
{
printf("\n a[%d][%d]=", i+1, j+1);
scanf("%d", &a[i][j]);
}
summa(N,a);
}
void summa(int n, int a1[ ][50])
{
int i,j,s;
puts("
ФУНКЦИЯ summa
");
/* ВЫЧИСЛЕНИЕ СУММЫ ПОЛОЖИТЕЛЬНЫХ ЭЛЕМЕНТОВ МАССИВА */
for (s=0,i=0; i<n; i++)
{
printf("\n");
for (j=0;j<n;j++)
if (a1[i][j]>0)
s+=a1[i][j];
}
printf("\a
getch();
СУММА = %d, Press any key... ",s);
}
Практическое занятие № 10
Тема. Классы памяти. Рекурсивные функции
Цель: Ознакомиться с областью действия переменных, рассмотреть основные классы
памяти и рекурсивные функции.
Краткие теоретические сведения по теме
10.1. Область действия переменных
Область действия переменной – это правила, которые устанавливают, какие
данные доступны из текущего места программы. Имеются три типа переменных:
глобальные, локальные и формальные. Область действия локальных переменных – это те
блоки, где локальные переменные объявлены. При выходе из блока локальная переменная
и ее значение теряются.
Формальные переменные – это параметры в заголовке функции пользователя.
Формальные параметры используются в теле функции так же, как локальные переменные.
Область действия формальных параметров – блок, являющийся телом функции.
Глобальные переменные объявляются вне какой-либо функции. Глобальные
переменные могут быть использованы в любом месте программы, но перед их первым
использованием они должны быть объявлены и проинициализированы. Область действия
глобальных переменных - вся программа с момента их объявления.
10.2. Классы памяти. Рекурсивные функции
В языке Borland C++ каждая переменная принадлежит к одному из четырех классов
памяти – автоматическая (auto), внешняя (extern), статическая (static), регистровая
(register). Тип памяти указывается ключевым словом (auto, extern, static, register),
стоящим перед спецификацией типа переменной. Например, register int a;
По умолчанию (если класс памяти для переменной не указан) переменная
относится к классу auto и будет размещена в стеке.
В языке Borland С++ аргументы при стандартном вызове функции передаются по
значению, т.е. в функцию передаются не оригиналы аргументов, а их копии. В стеке
выделяется место для формальных параметров функции и в это выделенное место при ее
вызове заносятся значения фактических аргументов. Затем функция использует и может
изменять эти значения в стеке. Но при выходе из функции измененные значения теряются.
Вызванная функция не может изменить значения переменных, указанных как фактические
аргументы при обращении к данной функции.
В случае необходимости, функцию можно использовать для изменения
передаваемых ей аргументов. В этом случае в качестве аргумента необходимо в
вызываемую функцию передавать не значение переменной, а ее адрес. А для обращения к
значению аргумента-оригинала использовать операцию «*».
Пример функции, в которой меняются местами значения аргументов x и y:
void z1(int
*x, int *y)
{ int t;
t=*x;
*x=*y;
*y=t;
}
Участок программы с обращением к данной функции:
int a=2, b=3;
void z1(int*, int*);
…
printf(“\n a=%d, b=%d”, a, b);
z1(&a, &b);
printf(“\n a=%d, b=%d”, a, b);
…
При таком способе передачи в вызываемую функцию аргументов их значения
теперь будут изменены, т.е. на экран монитора будет выведено:
a=2, b=3
a=3, b=2
В языке Borland С++ функции могут вызывать сами себя. В этом случае функция
называется рекурсивной.
Пример рекурсивной функции – вычисление факториала числа n!=1*2*3*…*n:
#include<stdio.h>
#include<conio.h>
#include<iostream.h>
double factor(double num)
{
if (num= =1) return 1.;
return num*factor(num-1.);
}
void main()
{ int arr[100]; //Максимум 100 чисел
int size, i;
cout<<”Введите количество чисел: ”<<endl;;
cin>>size;
cout<<“ Введите числа:”<<endl;
for(i=0;i<size;i++)
cin>>arr[i];
for(i=0;i<size;i++)
cout<<”Факториал ”<<arr[i]<<”=”<<factor(arr[i]);
getch();
}
/* Введите количество чисел: 3
Введите числа: 5 13 17
Факториал 5 = 120
Факториал 13 = 6227020800
Факториал 17 = 355687428096000
*/
Практическое занятие № 11
Тема. Командная строка. Параметры функции main()
Цель: Рассмотреть возможность передачи аргументов головному модулю запущенной
на выполнение программы – функции main(), посредством командной строки.
Краткие теоретические сведения по теме
Аргументы командной строки - это текст, записанный после имени запускаемого
на выполнение .com или .ехе файла, либо передаваемый программе с помощью опции
интегрируемой среды Borland C++ - arguments.
В качестве аргументов целесообразно передавать имена файлов, функций, текст,
задающий режим работы программы, а также сами данные (числа).
Borland C++ поддерживает три параметра функции main(). Для их обозначения
рекомендуется использовать общепринятые имена argc, argv, envp (но не запрещается
использовать любые другие имена). Вход в функцию main при использовании командной
строки имеет вид:
int main (int argc, char *argv[], char *envp[])
{ Тело функции}
Либо:
int main (int argc, char **argv, char **envp)
{ Тело функции}
Первый параметр (argv) сообщает функции количество передаваемых в командной
строке аргументов, учитывая в качестве первого аргумента имя самой выполняемой
программы (т.е. количество слов, разделенных пробелами). Отсюда следует, что
количество параметров не может быть меньше единицы, так как первый аргумент – имя
программы с полным путем к ней присутствует всегда.
Второй параметр (char **argv, char *argv[])является указателем на
массив их указателей на слова (т.е. сами параметры) из командной строки. Каждый
параметр хранится в виде ASCIZ – строки. Под словом понимается любой текст, не
содержащий символов пробела или табуляции. Запятые, точки и прочие символы не
рассматриваются как разделители. Последним элементом массива указателей является
нулевой указатель (NULL).
Например, пусть программа L13_5.exe запускается сл. образом:
C:\BorlandC\bin\L13_5.exe ddd
123
bcde
a+b
и заголовок функции main имеет вид:
void main(int argc, char *argv[])
{...}
тогда argc = 5 и создается массив из пяти указателей, каждый из которых указывает на
отдельное слово (аргумент).
Третий параметр функции main (char **envp) случит для передачи в программу
информации о системном окружении операционной системы, т.е. о среде, в которой
выполняется программа. Он является указателем на массив указателей, каждый указатель
определяет адрес, по которому записана строка информации о среде, определяемая
операционной системой. При знаком конца массива (как и в char *argv[]) является
нулевой указатель. Среда, в которой выполняется программа, определяет некоторые
особенности поведения оболочки и ядра операционной системы.
11.1. примеры программ с использованием параметров командной строки
Ниже приведены программы, демонстрирующие разные способы вывода на экран
аргументов командной строки.
//пример: вывести параметры командной строки посимвольно.
#include<iostream.h>
#include<conio.h>
#include <stdio.h>
void main(int argc, char *argv[])
{ int i,j;
for(i=0;i<argc;i++)
{ j=0;
while(argv[i][j]) cout<<argv[i][j++]<<” “;
}
getch();
}
/* пример: если в командной строке параметры отсутствуют, то вывести на экран текст:
«Командная строка: пустая», иначе вывести на экран текст: «Командная строка:» и далее
ее содержимое. */
#include<iostream.h>
#include<conio.h>
#include <stdio.h>
char ss[]=”Командная строка: ”; /*глобальная переменная, она видима в main,
но адрес ее будем получать из GetStrn*/
char *GetStrn(void){return ss;} //функция
void main(int argc, char *argv[])
{ char *s;
if (argc > 1)
s=argv[1];
else s=”пустая.”;
cout<<GetStrn()<<s; // cout<<ss<<s;
getch();
}
//пример: вывести содержимое командной строки на экран строками.
#include<stdio.h>
void main(int argc, char **argv)
{
int i;
cout<<”Имя программы:” <<*argv<<endl;
for(i=1;i<argc;i++)
cout<<”
“<<argv[i];
getch()
}
11.2 Структуры
Язык программирования Borland C++ поддерживает определяемые пользователем
структуры – структурированный тип данных. Он является собранием одного или более
объектов (переменных, массивов, указателей, других структур и т.д.), которые для
удобства работы с ними сгруппированы под одним именем.
Структуры облегчают написание и понимание принципов работы программ, а
также помогают сгруппировать данные, объединяемые каким-либо общим понятием
(например, данные: имя/фамилия/год рождения/ оценки по экзаменам можно поместить в
структуру данных о студенте). Структуры позволяют группу связанных между собой
переменных использовать как множество отдельных элементов, а также как единое целое.
Как и массив, структура представляет собой совокупность данных, но отличается от него
тем, что к ее элементам (компонентам) необходимо обращаться по имени и ее элементы
могут быть различного типа.
11.2. Объявления шаблонов структур
Общий синтаксис объявления шаблона структуры:
struct имя_ шаблона
{
тип1 имя_переменной1;
тип2 имя_переменной2;
// другие члены данных;
}
Структура объединяет логически связанные данные разных типов. Структурный тип
данных определяется описанием:
struct имя_структуры {
описание элементов;
};
Для выделения памяти под структуру надо определить структурную переменную:
struct имя_структуры имя_переменной;
При определении структур можно задавать начальные значения
элементам
структур. Для ввода значений элементов структур можно
использовать оператор cin>>
потокового ввода или оператор ввода scanf.
Над структурами допускаются следующие операции:
1) Взятие адреса структуры. Адрес структуры может быть получен путем применения
операции указатель (&) к структурной переменной.
2) Доступ к элементу структуры можно выполнить с помощью операций: точка (.) прямой доступ или стрелка ( -> ) - доступ по указателю.
Структурная переменная может использоваться так же, как и переменные типов
float,int,char и т.д.
Например:
struct date
/* объявление шаблона структуры типа date */
{ int day,month,year;
};
struct person
/*Шаблон структуры типа person */
{char fam[30],
//Фамилия
im[20],
//имя
otch[15];
//отчество
float weight;
//вес
int height;
//рост
struct date birthday;
//День рождения
}
Структура date имеет три поля типа int. Шаблон структуры person включает
поле birthday, которое, в свою очередь, имеет ранее объявленный тип данных: struct date.
Этот элемент (birthday) содержит в себе компоненты шаблона struct date.
11.3. Определение структур-переменных
Если описатель структуры записан до размещения всех функций в исходном файле,
то он будет доступен каждой из функций в этом файле. При определении структурной
переменной можно инициализировать (останавливать значения полям структуры).
Например
struct date { int day,month,year;};
d[5]={ { 1,3,1980}, { 5,1,1990}, { 1,1,2002} };
11.4. Размещение структурных переменных в памяти
Число байтов, выделенное под структурную переменную, не всегда равно сумме
дли отдельных ее элементов из-за некоторых особенностей работы процессора с данными
с фиксированной и плавающей точкой, что приводит к так называемому «выравниванию»,
размещению элементов с четного адреса.
При размещении структурной переменной в памяти для выравнивания на границу
слова компилятор оставляет между ее элементами и элементами массива структурных
переменных пустые байты для соблюдения следующих правил:



Структурная переменная (элемент массива структур) начинается на границе слова,
т.е. с четного адреса;
Любой элемент, кроме элементов типа char, также располагается с четного адреса и
соответственно имеет четное смещение от начала структурной переменной;
При необходимости в конец структурной переменной добавляется неиспользуемый
байт для того, чтобы общее число байтов, занимаемых переменной, было четным.
Практическое занятие № 12
Тема. Размещение полей битов в структурах
Цель: Рассмотреть использование в структурах особого типа полей – полей битов,
которые делают возможным доступ к отдельным битам более крупных объектов.
Краткие теоретические сведения по теме
Использование полей битов целесообразно в том случае, когда для хранения
информации в структуре данных достаточно несколько битов.
Общий синтаксис описания битового поля:
Тип [имя]: ширина;
Рассмотрим каждое поле описания.
ПОЛЕ «тип»
В Borland C++ для определения содержимого битового поля разрешено использовать тип,
интерпретируемый как целый: char, short, int, long ( с модификаторами signed, unsigned),
перечисления. В полях типа signed крайний левый бит является знаковым. Например, при
объявлении поля типа signed шириной в один байт (например, signed а:1;) оно будет
способно хранить только –1 или 0, так как любая ненулевая величина будет
восприниматься как (-1) .
ПОЛЕ «имя»
Ссылка на битовое поле выполняется по имени, указанному в поле «имя». Если имя в
данном поле не указано, то запрошенное количество бит будет отведено, но доступ к ним
будет невозможен.
Разрешается описание поля битов без имени и с шириной равной 0 – в этом случае
следующее поле будет начинаться с границы целого.
ПОЛЕ «ширина»
Каждому полю выделяется точно столько битов, сколько указано в поле «ширина».
Данное выражение должно иметь неотрицательное значение. Это значение не может
превышать числа разрядов, требуемого для представления значения типа, указанного в
поле «тип».
Объявление структуры, содержащей поля битов имеет следующий вид:
struct имя_структуры
{ тип [имя 1]:ширина;
тип [имя 2]:ширина;
тип [имя N]:ширина;
};
Рассмотрим примеры объявления битовых полей. Объявление шаблона:
struct bitfld1
{ int a:2;
unsigned b:3;
};
Объявление шаблона и определение переменных:
struct bitfld2
{ int a:1;
unsigned b:2;
int :4;
int c:4;
unsigned d:10;
} bf1, bf2;
12.1. Размещение полей битов в памяти
Поля битов располагаются в машинном слове справа налево, т.е. в направлении от
младших разрядов к старшим в очередности их объявления. Если обратиться к общему
объявлению структуры с полями битов описанному выше, то размещение полей в памяти
можно представить следующим образом:
Старшие
Разряды машинных слов
Младшие
Имя N....................................................имя2 имя1
Если общая ширина полей битов в структуре не кратна восьми (т.е. некоторые битовые
поля будут не полностью занимать байт), то соответствующее количество битов данного
байта будет свободно. Например:
struct EXAMPLE
{
unsigned a:3;
int b:2;
};
Данная запись обеспечит следующее размещение полей в памяти:
Разряды байта
Знаковый бит
7
6
5
Не используются
4
3
b
2
1
0
а
12.2. Доступ к полям битов. Инициализация полей битов. Недопустимые действия
Доступ к полям битов осуществляется так же, как и к другим элементам
структурной переменной, т.е. с помощью операции выбора элемента “.”. Например:
struct EXAMPLE
{ int a:2;
unsigned int b:3;
unsigned int c:6;
int d:1;
} exam;
Доступ к объявленным полям в данном примере происходит следующим образом:
Exam.a – доступ к полю “a”;
Exam.b – доступ к полю “b” и т.д.
При работе с битовыми полями недопустимы:
 Массивы битовых полей;
 Указатели на битовые поля;
 Функции, возвращающие битовые поля.
Практическое занятие № 13
Тема. Объединения
Цель: Ознакомиться с понятием объединения, с объявлением, инициализацией
объединения. Рассмотреть указатель на объединение, размещение объединений в
памяти.
Краткие теоретические сведения по теме
Объединение – это особый тип данных языка Borland C++, называемый составным
типом. Фактически объединение – это переменная, которая позволяет в разные моменты
времени хранить значения различных типов. Объединение может содержать и
структурные переменные. Его особенностью является то, что все данные, включенные в
объединение, располагаются в памяти с одной и той же границы (адреса), т.е. они
перекрывают друг друга.
Объединение обычно является частью структуры. Отдельные данные, входящие в
объединение, в дальнейшем будем называть полями.
13.1. Объявление объединения
Как и любая переменная, объединение должно быть определено. Определение состоит из
двух шагов:
 Задание шаблона объединения;
 Собственно описание переменной-объединения.
В дальнейшем непосредственно переменную – объединение будем называть просто
объединением.
Шаблон создается при помощи ключевого слова union с использованием следующего
синтаксиса:
Union [<имя>]
{тип поле1;
тип поле2;
...
тип полеN;
};
где <имя> - имя описываемого шаблона, удовлетворяющее правилам задания
идентификаторов языка Borland C++; тип – любой тип языка Borland C++; поле1...полеN –
имена полей данных, входящих в объединение, удовлетворяющие правилам задания
идентификаторов языка Borland C++.
Поскольку описание шаблона на самом деле является оператором языка, то в конце
ставится точка с запятой.
Например, описание простейшего шаблона объединения имеет вид:
union EXAMPLE
{int i;
char ch[2];
};
Поле, являющееся объединением, называют вложенным объединением. Шаблон
вложенного объединения должен быть уже известен компилятору. Например:
union ENCLOSE
{char ch;
int im[10];
};
union EXAMPLE
{char chm[20];
union ENCLOSE enc1;
}enclose_union;
13.2. Инициализация объединения
При определении объединения разрешается выполнять его инициализацию.
Особенностью инициализации объединения является то, что переменная может быть
проинициализирована значением только для первого описанного поля.
Например:
union EXAMPLE1
{ int i;
char ch[4];
float f;
};
union EXAMPLE1 exam = {12346};
В данном примере вначале был задан шаблон с именем EXAMPLE1, затем по
заданному шаблону была определена переменная exam и проинициализирована целым
числом 12346, так как первое поле данного объединения имеет тип int. Попытка какимлибо образом проинициализировать данную переменную значениями для полей ch или f
приведет к ошибки.
Синтаксис языка Borland C++ позволяет одновременное задание шаблона,
определение переменных по данному шаблону и их инициализацию.
Например:
union EXAMPLE
{ int i;
char ch[2];
}; uni = {9650}
13.3. Указатель на объединение
Описание шаблона объединения вводит по существу новый тип данных. Значит,
вполне допустимо использовать указатель на введенный тип. Как и любой другой
указатель иных типов данных, указатель на объединение занимает либо два байта (near),
либо четыре байта (far).
Описание указателя на объединение не отличается от описания указателей на
другие типы:
<типы> *<имя_указателя >;
где <тип> - задает имя шаблона объединения, указатель будет данного типа; <
имя_указателя > - имя определяемого указателя. Например:
union EXAMPLE
{ int i;
char ch[2];
}; *pexam;
union EXAMPLE *puni1, *puni2;
Указатели на объединение обычно используются для доступа к объединениям,
размещенным в динамически выделенной памяти.
13.4. Размещение объединений в памяти
Все элементы объединения размещаются в одной и той же области памяти
одного и того же адреса. Память, которая выделяется под объединение, определяется
размером наиболее длинного из элементов данного объединения. Например, если описать
объединение вида
union EXAMPLE_1
{ int i;
float f;
double d;
char ch;
}uni;
то компилятор выделит под объединение uni 8 байтов, так как данное объединение
содержит поле с максимальной длиной 8 байтов (поле типа double).
Если же объявить следующее объединение:
union EXAMPLE_2
{ long double ld;
char let[20];
}uni;
то компилятор выделит под объединение uni 20 байтов, так как в этом случае
максимальную длину имеет поле, являющееся символьным массивом из элементов, для
размещения которго необходимо 20 байтов, в то время как поле типа long double требует
всего 10 байтов.
13.5 Динамическое использование памяти. Библиотечные функции
Одним из способов хранения информации является использование системы
динамического выделения памяти языка Borland C++. Область свободной памяти,
доступной для выделения, находится между областью памяти, где размещается
программа. Эта область называется кучей или хипом (от англ. heap – куча).
Ядром динамического выделения памяти в Borland C++ являются функции№
объявленные в стандартной библиотеке в заголовочном файле stdlib.h, - malloc(), calloc(),
realloc(), free().
В языке Borland C++ есть операция, с помощью которой можно определить размер
участка памяти в байтах, который компилятор отводит под массив после его объявления,
это операция sizeof. Формат записи:
sizeof(параметр);
параметр – тип объекта или его идентификатор (только не имя функции). Операция sizeof
позволяет определить размер объекта по имени или типу, результатом является размер
памяти в байтах (тип результата int).
Если указан идентификатор сложного объекта (массив, структура, объединение), то
получаем размер всего сложного объекта. Например:
sizeof(int)
->
2 байта,
int b[5]; sizeof(b)
->
10 байт;
int c[3][4]; sizeof(c) ->
24 байта.
Операция sizeof применяется при динамическом распределении памяти:
float *x;
// Указатель массива
int n;
// Количество элементов массива
x=(float*)calloc(n,sizeof(float));
// Захват и очистка
памяти
Прототипы функций работы с памятью находятся в библиотеке alloc.h, рассмотрим
основные из них:
void *calloc(unsigned n, unsigned m);
- возвращает указатель на начало области
памяти для размещения n элементов по m байт каждый, при неудачном завершении
возвращает значение NULL;
void *malloc(unsigned n); - возвращает указатель на блок памяти длиной n байт,
при неудачном завершении возвращает значение NULL;
void *realloc(void *bf, unsigned n);
- изменяет размер ранее выделенной
памяти с адресом начала bf на n байт;
void free(void *bf); - освобождает ранее выделенный блок памяти с адресом bf;
coreleft(void); - возвращает значение объема неиспользованной памяти (тип
возвращаемого результата unsigned – для моделей памяти tiny, small, medium; unsigned
long – для других моделей памяти).
Приведем пример динамического размещения одномерного массива
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<alloc.h>
void main(void)
{ int i,n; float *a;
puts(“\n Input n:”); scanf(“%d”,&n);
printf(“\n Свободная память -%d”,coreleft());
a=(float *)calloc(n,sizeof(float));// захват памяти
printf(“\n Array a \n”);
for(i=0; i<n; i++)
{
*(a+i)=(float)random(10);
// диапазон от 0 до 10
printf(“ %d“, a[i]);
}
printf(“\n Память после захвата -%d”,coreleft());
free(a);
// освобождение памяти
getch();
}
Практическое занятие № 14
Тема. Файлы
Цель: Изучить способы создания и работы с файлами в языке Borland C++.
Краткие теоретические сведения по теме
14.1. Понятие файла и файловой системы
Файл – это набор данных, размещенный на внешнем носителе и рассматриваемый
в процессе обработки и пересылке как единое целое.
Прежде, чем работать с файлом его нужно открыть для доступа, т.е. создать и
инициализировать область данных, которая содержит информацию о файле: имя, путь и
т.д.
В алгоритмическом языке Borland C++ это делает функция fopen. Она связывает
физический файл на носителе, например B:\LR7.CPP, с логическим именем в программе.
Логическое имя – это указатель на файл, т.е. на область памяти, где храниться
информация о файле. Указатели на файлы необходимо объявлять. Формат объявления
такого указателя следующий:
FILE *указатель на файл;
Например.
FILE *f;
f=fopen ("B:\LR7.СPP", "w");
Символ "w" определяет право доступа к открываемому файлу. В данном случае
открывается файл LR7.СPP на диске B:\ только для чтения.
В Borland C++ используются следующие коды, устанавливающие режимы
доступа к открываемым файлам:
Символ
Описание
R
Файл открывается только для чтения. Если нужного файла на диске нет, то
возникает ошибка.
W
Файл открывается только для записи. Если файла с заданным именем нет, то он
будет создан, если же такой файл существует, то перед открытием прежняя
информация уничтожается.
A
Файл открывается для дозаписи в его конец новой информации.
r+
Файл открывается для редактирования его данных. Возможны и запись, и
чтение информации.
w+
То же, что и для r+.
a+
То же, что и для a, только запись можно выполнять в любое место файла.
Доступно и чтение файла.
T
Файл открывается в текстовом режиме. Указывается поле r, w, a, r+, w+, a+.
B
Файл открывается в двоичном режиме. Указывается поле r, w, a, r+, w+, a+.
Текстовый режим отличается от двоичного тем, что при открытии файла как
текстового пара символов « перевод-строки», «возврат каретки» заменяется на один
символ: « перевод-строки» для всех функций записи данных в файл, а для всех функций
вывода символ «перевод-строки» теперь заменяется на два символа: « переводстроки»,«возврат каретки».
По умолчанию файл открывается в текстовом режиме.
После работы с файлом, доступ к файлу необходимо закрыть. Закрывает файл в
языке Borland C++ функция fclose. Например, из предыдущего примера ее закрывается
так: fclose (f);
Для открытия нескольких файлов введена функция, объявленная следующим
образом:
Void fcloseall(Void);
Если требуется изменить режим доступа к файлу, то для этого сначала
необходимо закрыть данный файл, а затем вновь его открыть, но с другими правами
доступа. Для этого используют стандартную функцию freopen, описанную в stdio.h как
FILE* freopen (char filename, chov*mode, FILE *stream). Эта функция сначала закрывает
файл, связанный с потоком stream (как это делает функция fopen), а затем открывает файл
с именем filename и правами доступа mode, записывая информацию об этом файле в
переменную stream.
В алгоритмическом языке Borland C++
имеется возможность работы с
временными файлами, т.е. с такими, которые нужны только в процессе работы программы
и которые надо удалить после выполнения части вычислений. в этом случае используют
функцию tmpfile со следующим объявлением: FILE* tmpfile (void).
Функция tmpfile создает на диске временный файл с правами доступа «w+b» и
возвращает указатель на управляющий блок по типу атрибута FILE. После завершения
работы программы или после закрытия временного файла он автоматически удаляется из
диска.
Все действия по чтению/записи данных в файл можно разделить на три группы:
1. Операции посимвольного ввода-вывода,
2. Операции построчного ввода-вывода,
3. Операции ввода-вывода по блокам.
Ниже приведены основные функции, применяемые в каждой из указанных трех групп
операций.
14.1.1. Посимвольный ввод-вывод
В операциях посимвольного ввода-вывода происходит прием одного символа из
файла или передача одного символа в файл.
Функция
Действие функции
int fgets(FILE *fp)
Чтение и возврат символа из открытого файла
int fgetchov(void)
Читает и возвращает символ из файла stdin.
int ungetc(int ch, FILE *fp)
Возвращает символ ch в файл. Следующая операция чтения
символа из файла вернет этот символ.
int fputs(int ch, FILE *fp)
Записывает в файл код ch символа.
14.1.2. Построчный ввод-вывод
В операциях построчного ввода-вывода за один прием происходит перенос из
файла (или в файл) строк символов.
Функция
Действие функции
int gets (char *S)
Читает байты из файла stdin и записывает их в строку S
до тех пор, пока не встретит символ ' \n ', который
заменяется на нуль – терминатор.
int fgets (char *S int m, FILE *fp) Извлекает байты из файла, описываемого fp, и
записывает их в строку S до тех пор, пока не встретит
символ ' \n ' или пока не будет считана m байтов.
int fputs (char *S, FILE *fp)
Записывает в файл байты из строки S до тех пор, пока
не встретится нуль-терминатор, который в файл не
переносится и на символ ' \n ' не заменяется.
int puts (char *S)
Записывает в файл, stdout байты из строки S до тех пор,
пока не встретится нуль-терминатор, который в файл
переносится и заменяется на символ ' \n '.
Блоковый ввод-вывод
В операциях блокового ввода-вывода работа происходит с целыми блоками
информации.
Действие функции
Функции
int fread (void *ptv, int Считывает n блоков по size байт каждый из файла fp, в область
size, int n, FILE *fp)
памяти, на которую указывает указатель ptv (необходимо заранее
отвести память под считываемый блок).
int fwrite (void *ptv, Записывае n блоков по size байт каждый из области памяти, на
int size, int n, FILE которую указывает ptv, в открытый файл fp.
*fp)
Пример работы с файлами. Следующая программа формирует целочисленный бинарный
файл, дозаписывает в его окончание новые данные, и выводит сохраненные данные на
печать:
#include <stdio.h>
#include <conio.h>
void main(void)
{
int a=1, b=20, c, d;
FILE *in, *out, *add;
clrscr();
/* ........ ЗАПИСЬ ЧИСЕЛ В ФАЙЛ ......*/
in=fopen("lr8.dat","wb");
fprintf(in,"%d %d \n",a,b);
fclose(in);
puts("ЧИСЛА a, b ЗАПИСАНЫ В ФАЙЛ");
puts("Press any key...");
getch();
/* ......... ЧТЕНИЕ ЧИСЕЛ ИЗ ФАЙЛА ........*/
out=fopen("lr8.dat","rb");
fscanf(out,"%d%d", &c, &d);
printf("\n a=%d b=%d ", c, d);
fclose(out);
puts("\n ЧИСЛА ПРОЧИТАНЫ ИЗ ФАЙЛА");
puts("Press any key...");
getch();
/* ......... ДОПОЛНЕНИЕ ФАЙЛА ..........*/
add=fopen("lr8.dat","a");
puts("ВВЕДИТЕ ЧИСЛА ЦЕЛОГО ТИПА c и d");
scanf("%d%d",&c,&d);
fprintf(add,"%d %d \n",c,d);
printf("\n c=%d d=%d \n ",c,d);
fclose(add);
puts("ЧИСЛА c и d ДОПИСАНЫ В ФАЙЛ");
puts("Press any key...");
getch();
/* ......... ЧТЕНИЕ ЧИСЕЛ ИЗ ФАЙЛА ........*/
out=fopen("lr8.dat","rb");
fscanf(out,"%d%d%d%d", &a, &b, &c, &d);
printf("\n a=%d b=%d ", a, b);
printf("\n c=%d d=%d ", c, d);
fclose(out);
puts("\n РАСШИРЕННЫЙ ФАЙЛ!");
puts("Press any key...");
getch();
}
Практическое занятие № 15
Тема. Графические возможности языка Borland C++
Цель: Изучение работы дисплея в графическом режиме.
Краткие теоретические сведения по теме
15.1. Графический режим работы
При работе в графическом режиме экран дисплея представляет собой матрицу
точек (пикселов - pixel) - т.е. матрицу отображаемых точек. При этом число столбцов и
строк пикселов (разрешение экрана дисплея) зависит от режима работы видеоадаптера.
Можно управлять цветом каждого пиксела, задавая цвета фона, рисунка и заполнения
замкнутых областей экрана дисплея, а также создавать эффект движения изображений.
За начало координат экрана дисплея в графическом режиме принимается верхний
левый угол с координатами x=0 и y=0, где x - координата по горизонтали, y - координата
по вертикали точки ( пиксела). Во всех примерах программ, приведенных далее по тексту,
нулевые координаты присваиваются верхнему левому углу создаваемого графического
окна. Содержимое библиотеки графических функций в алгоритмическом языке Borland
C++ подразделяется на немобильную группу функций (функции зависят от типа
адаптера) и на мобильную группу функций.
Немобильная группа графических программ представляет собой BGI драйвер (Borland
Graphics Interface).
Драйвер - это обработчик прерывания 10h, он должен дополнить системный
обработчик до того, как будут использоваться мобильные графические функции. Перед
завершением программы таблица векторов прерываний восстанавливается.
Основные функции BGI-драйвера - установка и обновление ряда внешних
переменных, которые могут изменяться как функциями системного обработчика
прерывания 10h (например, при переключении видеорежима или при изменении
регистров палитры), так и мобильными функциями библиотеки графики
алгоритмического языка Turbo C (TC) или Borland C++.
Для различных типов адаптеров применяются различные драйверы:
CGA.BGI - драйвер для CGA и MCGA, EGAVGA.BGI - драйвер для адаптеров
EGA,VGA, HERC.BGI - драйвер для монохромных адаптеров Hercules.
Графические функции мобильной группы подразделяются на
1. Функции для подготовки графической системы и перехода в текстовый режим.
2. Функции для получения изображений на экране дисплея.
3. Функции для установки параметров изображения( вид штриховки, толщина линий и
т.д.).
4. Функции для определения параметров режимов и изображений.
15.2. Функции для подготовки графической системы
Перед использованием графических функций необходимо инициализировать
систему графики. Графические режимы,
поддерживаемые библиотекой графики,
задаются символическими константами, описанными в файле
<graphics.h> в перечислимом типе graphics_mode.
Инициализация графической системы производится функцией initgraph(), которая
загружает графический драйвер и переключает экран дисплея в требуемый графический
режим. Прототип функции initgraph:
initgraph(&g_driver,&g_mode," ");
В двойных апострофах (третий параметр в прототипе функции) необходимо указать
путь (маршрут) к графическому драйверу. Если указать пробел, то графический драйвер
должен быть в текущем каталоге. Первый параметр - &g_driver - тип графического
драйвера:1 - CGA, 3 - EGA, 9 - VGA и т.д. Второй параметр - &g_mode - графический
режим
( рассмотрим только для VGA драйвера):
VGA
0
640x200
VGAMED 1
640x350
VGAHI
2
640x480
Запись типа 640x200 - это разрешающая способность экрана дисплея в графическом
режиме (число строк умножить на число столбцов).
Для задания автоматического режима графики необходимо записать:
int g_diver=DETECT,g_mode;
Для завершения работы в графическом режиме необходимо применить функцию
closegraph();
15.3. Основные функции для получения изображения
1. Вычерчивание окружности: circle(x,y,r);
2. Вычерчивание закрашенного прямоугольника: bar(x1,y1,x2.y2);
3. Вычерчивание параллелепипеда: bar3d(x1,y1,x2,y2,глубина,p); p=0 или p=1 - верхняя
грань отображается (не отображается)
4. Вычерчивание линии: line(x1,y1,x2,y2);
5. Вычерчивание точки: putpixel(x,y,цвет);
6. Вычерчивание прямоугольника: rectangle(x1,y1,x2,y2);
7. Вывод текста: outtext(x,y,"текст");
8. Установка указателя на экране дисплея: moveto(x,y);
9. Очистка экрана дисплея: cleardevice(void);
10. Заполнение ранее заданным наполнителем замкнутой области:
floodfill(x,y,c); c - номер цвета линии, ограничивающей область.
15.4. Основные функции для установки параметров изображения
1. Установка цвета линий: setcolor(n);
2. Установка цвета фона: setbkcolor(n);
3. Установка стиля наполнителя замкнутых линий:
setfillstyle(номер наполнителя 0 - 12 ,цвет);
4. Установка толщины линий: setlinestyle(стиль линии,0,толщина);
0 - непрерывная, 1 - из точки, 2,3 - штрих,
5. Установка стиля текста: settextstyle(шрифт 0-4, направление: 0 – горизонтальное, 1 вертикальное, размер 1-0);
Вопросы для подготовки к экзамену
Парадигма ООП. Специфика интерфейса ОО программ.
Преимущества ООП.
Понятие класса. Отношение "объект - класс".
Понятие полиморфизма. Проявления полиморфизма.
Механизм наследования.
Модификатора доступа и наследования. Как изменяются атрибуты
элементов класса при наследовании?
7. Смысл инкапсуляции.
8. Что такое объектно-ориентированное программирование и каковы его
главные отличительные признаки?
9. Что такое инкапсуляция?
10.Что такое методы?
11.Что такое экземпляры объекта?
12.Что такое наследование в ООП?
13.Что такое раннее и позднее связывание?
14.Что такое виртуальный метод?
15.Что такое полиморфизм?
16.Что такое конструкторы и деструкторы?
17.В чем разница между struct, class и union?
18.Что такое конструктор, деструктор и когда они вызываются?
19.Что такое встраиваемая функция? В чем ее преимущества и
недостатки?
20.Какие два способа существуют для создания встраиваемой функции?
21.Как можно передать в функцию в качестве аргумента адрес объекта?
22.Что такое дружественная функция?
23.Когда следует переопределять операторы с помощью дружественных
функций, а когда с помощью функций элементов класса?
24.Что происходит при присваивании одного объекта другому (без
переопределенной операции =) и какие побочные эффекты могут
возникнуть?
25.При передаче объекта в функцию в качестве аргумента создается копия
этого объекта. Изменение копии в теле функции не отражается на
оригинале. Возможно ли нарушение этого правила?
26.Какая функция может иметь доступ к защищенным членам одного
класса? ... двух классов?
27.Что такое указатель this. Приведите пример использования этого
указателя.
28.Для чего необходимы операторы new и delete. В чем их отличие от
функций malloc() и free()?
29.Что такое ссылка? Какое имеется преимущество при использовании
ссылки в качестве параметра функции, а в чем недостаток? В чем
разница между ссылкой и указателем?
1.
2.
3.
4.
5.
6.
30.Назовите причины, по которым может понадобиться перегрузка
конструкторов и деструкторов (в одном классе).
31.Какова основная форма конструктора копирования и когда он
вызывается?
32.Что такое аргумент по умолчанию? Как аргумент по умолчанию связан
с перегрузкой функций?
33.Почему может потребоваться перегрузка оператора присваивания?
34.Как можно ли изменить приоритет перегруженного оператора?
35.Что такое виртуальная функция?
36.Какие функции не могут быть виртуальными?
37.Что такое абстрактный класс и чем может быть вызвана необходимость
построений абстрактного класса?
38.Чем виртуальные функции отличаются от перегружаемых?
39.Раннего и позднее связывание.
40.Совместимость типов. Какую роль в достижении совместимости
объектов могут играть абстрактные классы?
41.Почему шаблоны называют параметризованными типами?
42.Когда следует в программе применять шаблоны, а когда нет?
43.Чем шаблоны лучше макроподстановок?
44.Для каких типов данных может применяться конкретный шаблон, а для
каких нет?
45.В чем разница между классом и шаблоном класса?
46.Что может выступать в качестве параметра для шаблона класса?
47.В чем основное преимущество использования библиотеки потокового
ввода-вывода?
48.Как работает операция внесения (на примере)?
49.Что такое манипулятор ввода - вывода?
50.Организация файлового и строкового потока.
1. А.Я. Архангельский Программирование в Borland C++ - М., Бином, 2003
2. Бруно Бабэ Просто и ясно о Borland C++ - М., Бином
Использованная литература
1. Олифер В.Г., Олифер Н.А. Компьютерные сети. Принципы, технологии; протоколы. СПб.: Питер, 2006. - 692с.
2. Хелд Г. Технологии передачи данных. СПб.: Питер, 2003. - 720 с.
3. Гук М. Аппаратные средства локальных сетей. Энциклопедия - СПб.: Питер, 2005.576 с.
4. Таненбаум Э. Компьютерные сети. -СПб.: Питер, 2003. - 992с.
5. Закер К. Компьютерные сети. - СПб.: БХВ-Петербург, 2005. – 1008 с.
6. Столингс В. Передача данных. -СПб.:Питер, 2004. - 750 с.
7. Столингс В. Современные компьютерные сети. - СПб.:Питер,2004. -750 с.
8. Камер Д.Э. Компьютерные сети и Intemet.-M.: «Вильямс», 2002.- 640 с.
9. Кульгин М. Компьютерные сети. -СПб.: Питер, 2004. - 462 с.
10. Паркер Т., Сиян К. TCP/IP. Для профессионалов. - СПб.: Питер, 2004. - 859 с.
11. Хелеби С., Мак-Ферсон Д. Принципы маршрутизации в Интернет. - М.: Изд. дом
«Вильямс», 2001. - 448 с.
Download