Академический Документы
Профессиональный Документы
Культура Документы
Давыдов
Программирование
и основы
алгоритмизации
Рекомендовано УМО по образованию в области радиотехники,
электроники и биомедицинской техники и автоматизации
в качестве учебного пособия для студентов высших
учебных заведений, обучающихся по специальности
«Управление и информатика в технических системах»
Давыдов, В.Г.
Д 13 Программирование и основы алгоритмизации: Учеб. посо-
бие/В.Г. Давыдов. — М.: Высш. шк., 2003. — 447 е.: ил.
ISBN 5-06-004432-7
Учебное пособие написано в соответствии с разработанной с участием автора
примерной программой курса «Программирование и основы алгоритмизации», утвер
жденной Министерством образования Российской Федерации для подготовки бакалав
ров и специалистов по направлениям 5502 и 6519 «Автоматизация и управление».
Его цель состоит в поэтапном формировании у студентов следующих слоев зна
ний и умений — знание основных понятий программирования (слой 1), знание базо
вого языка программирования C++ (слой 2) и умение решать задачи на ЭВМ (слой 3).
Для удобства преподавателей и студентов приведено по 20 вариантов контроль
ных заданий по основным разделам курса, заданий на выполнение программных
проектов и приведены тестовые экзаменационные вопросы. Прилагаемый к учебно
му пособию компакт-диск содержит описание ПМ-ассемблера, его интегрированную
среду программирования, полные тексты демонстрационных программ автора и др.
Для студентов высших учебных заведений, обучающихся по специальности
210100 — «Управление и информатика в технических системах».
УДК 004.4
ББК 32.965
Учебное издание
Давыдов Владимир Григорьевич
ПРОГРАММИРОВАНИЕ И ОСНОВЫ АЛГОРИТМИЗАЦИИ
Редактор ТВ. Рысева, Художник ТС. Лошаков
Лицензия и д № 06236 от 09.П.01
Изд. № РЕНТ-183. Подп в печать 14.08.03. Формат 60x88Vi6. Бум. газетная. Гарнитура «Тайме».
Печать офсетная. Объем 27,44 усл. печ. л., 27,94 усл. кр.-отг.. Тираж 3000 экз. Заказ № 3184.
ФГУП «Издательство «Высшая школа», 127994, Москва, ГСП-4, Неглинная ул., 29/14.
Тел.: (095) 200-04-56. E-mail: mfo@v-shkola.ru http://www.v-shkola.ru
Отдел реализации: (095) 200-07-69, 200-59-39, факс: (095) 200-03-01. E-mail: sales@v-shkola.ru
Отдел «книга-почтой»: (095) 200-33-36. E-mail: bookpost@v-shkola.ru
Отпечатано в ФГУП ордена «Знак Почета» Смоленской областной типографии им. В.И. Смирнова.
214000, г. Смоленск, пр-т им. Ю. Гагарина, 2.
Автор
1. ВВЕДЕНИЕ
3 . 4,6)-^^1,^^06) 42)=?
в соответствии с табл. 1 заменяем шестнадцатеричные цифры
тетрадами
1010 1111 0001 , 1 1 1 1 1110
и получаем
^2) =101011110001,1111111(2)
4. 48)=710,526(,,) 42)=?
Этот пример предлагаем выполнить самостоятельно.
В о з м о ж е н б ы с т р ы й и п р о с т о й п е р е в о д ч и с е л из в о с ь м е р и ч н о й
системы в шестнадцатеричную систему счисления и обратно. Такой
п е р е в о д м о ж н о в ы п о л н и т ь в д в а э т а п а - с н а ч а л а из и с х о д н о й с и с т е
м ы с ч и с л е н и я в д в о и ч н у ю с и с т е м у и з а т е м из д в о и ч н о й в н о в у ю с и с
тему счисления. Примеры такого рода также предлагаем выполнить
самостоятельно.
Я з ы к и п р о г р а м м и р о в а н и я д е л я т с я на д в е г р у п п ы :
1. Машинно-зависимые языки, которые можно применять на
одной ЭВМ или на ограниченном подмножестве машин с одинако
вой архитектурой.
2. Машинно-независимые языки - их можно использовать на
любой ЭВМ. Языки этой группы называют универсальными языка
ми.
Машинно-зависимые языки^ в зависимости от их близости к
машинным языкам, делятся на три группы:
• машинные языки (языки нулевого уровня);
• ассемблерные языки (языки первого уровня или языки типа 1:1,
последнее означает, что одна ассемблерная команда после транс
ляции порождает ровно одну машинную команду);
• макроассемблеры (языки второго уровня или языки типа \\п).
Аналогично, маилинно-независимые языки включают сле
дующие группы языков:
• Процедурные языки (третий уровень): Си, C+-I-, Паскаль,
ФОРТРАН, БЭЙСИК и др. Процедурные языки требуют детальной
разработки алгоритма решения и, по существу, являются языками
для записи алгоритмов решения задач.
• Проблемные языки (четвертый уровень) или языки типа "заполни
бланк". Это языки описания задач, специализированные языки.
Используя подобный язык программирования, пользователь со
общает только, какую задачу надо решить и с какими данными.
Как решить задачу - "знает" язык. В качестве примера проблемно
го языка можно назвать язык ПРОСПО, разработанный фирмой
IBM для программирования систем управления производствен
ными процессами.
• Универсальные языки (пятый уровень): ПЛ/1, АЛГОЛ-68, Ада и
др. При создании универсальных языков в их состав включили все
лучшее, что имелось на момент создания в процедурных языках.
н а п е ч а т а т ь для к о н т р о л я в в е д е н н ы е д а н н ы е , в ы ч и с л и т ь и н а п е ч а т а т ь
Е=АВ + С/\,5
Система команд м а ш и н ы П М с о д е р ж и т н е с к о л ь к о д е с я т к о в
р а з л и ч н ы х одноадресных команду из к о т о р ы х в т а б л . 2 п р и в о д я т с я
л и ш ь те, к о т о р ы е б у д у т н е о б х о д и м ы д л я р е ш е н и я у к а з а н н о й з а д а ч и .
Т а б л . 2. Т а б л и ц а к о д о в о п е р а ц и й ( К О П )
КОП Выполняемое действие (обозначение команды)
01 Передача слова из ОЗУ в регистр А арифметико-логического устройства
АЛУ (ЧТЕНИЕ)
02 Передача слова из регистра А АЛУ в ОЗУ (ЗАПИСЬ)
03 Слолсение содержимого регистра А АЛУ с операндом, заданным в коман-
де. Результат помещается в регистр А АЛУ (+)
04 Вычитание ( - ). Операция выполняется аналогично сложению
05 Умножение ( * ).Операция выполняется аналогично сложению
06 Деление ( / ).Операция выполняется аналогично сложению
07 Ввод слова с устройства ввода в ОЗУ (ВВОД)
08 Вывод слова из ОЗУ в устройство вывода (ВЫВОД)
П р и с о с т а в л е н и и п р о г р а м м ы р е ш е н и я з а д а ч и на м а ш и н н о м
языке требуется решить следующие вопросы:
1. Р а с п р е д е л и т ь п а м я т ь д л я д а н н ы х , о б р а б а т ы в а е м ы х п р о
г р а м м о й , н а п р и м е р , в н а ч а л е п а м я т и (табл. 3)
2. Р а с п р е д е л и т ь п а м я т ь д л я к о м а н д п р о г р а м м ы . Н а п р и м е р , у с
ловимся размещать программу, начиная с адреса 01000 (для удобст
ва и с п о л ь з у е т с я д е с я т и ч н ы й а д р е с в м е с т о д в о и ч н о г о а д р е с а ) .
3. С о с т а в и т ь с о б с т в е н н о п р о г р а м м у , т.е. з а п и с а т ь к о д ы ма
ш и н н ы х к о м а н д с у к а з а н и е м м е с т а их р а з м е щ е н и я в О З У . Н а п о м и
наем, что р е а л ь н о в Э В М и с п о л ь з у ю т с я д в о и ч н ы е к о д ы к о м а н д и
10
адресов, но мы для собственного удобства воспользуемся и здесь
десятичными кодами. Машинная программа приведена в табл. 4.
Проанализировав содержимое табл. 3, оценим возможности и
особенности программирования на машинных языках.
В оперативном запоминающем устройстве (ОЗУ) между ме
стом размещения данных и команд программы осталась "дырка" -
ячейки с адресами 00005...00999, которую для полного использова
ния памяти нужно ликвидировать. Однако заранее оценить размер
памяти, необходимой для размещения данных и команд, нельзя или,
по крайней мере, очень трудно.
11
2. Реальные системы команд ЭВМ громоздки (сотни различ
ных команд), а использование двоичных кодов команд программы
трудоемко и ненаглядно. Машинную программу трудно восприни
мать (см. табл. 4 без левого и правого столбцов).
Из-за отмеченных недостатков машинные программы давно
перестали писать. Вместе с тем отметим достоинство программ, на
писанных с использованием машинных языков, - они имеют наи
большее быстродействие и требуют минимальных затрат памяти. Но
это может быть достигнуто только ценой больших затрат времени
квалифицированного программиста.
Многие из названных выше недостатков машинных программ
устраняются при программировании на ассемблерных языках.
12
ностями ассемблера и, дополнительно, мощным аппаратом макро
вызовов и макроопределений. Поясним возможности указанного ап
парата примером.
Пример. Предположим, что часто встречается вычисление
R^{X^+Y^)/{X'Y)
Таблица
символов (ТС)
Текст программы
на машинном
языке
Рис. 1. Схема трансляции в два прохода
[Г 2 10 11 20 21 40
к МЕТКА ОПЕРАЦИЯ ОПЕРАНД КОММЕНТАРИЙ
13
2 10 11 20 21 40 41
МЕТКА ОПЕРАЦИЯ ОПЕРАНД КОММЕНТАРИЙ
SUB(A, В, С)
SUB(E, F, G)
14
PROGRAM PRMF7 7
С Пример для языка ФОРТРАН? 7. Вычисляем D : = A ' ^ B - h C / 2 . 0
REJLL А, В, С, D
READ *г ^f Br С
PRINT *r А, В, С
D = А ^ В + С / 2.0
PRINT *, D
STOP
END
2. ЯЗЫК ПРОГРАММИРОВАНИЯ
ВЫСОКОГО УРОВНЯ C++
16
2.1. Введение. Структурное и модульное
программирование
Аль Хорезми
Структурное Дейкстра
программирование
Ветвления Следование Циклы
if - then - else while
switch do - while
for
Рис. 2. Алгоритм и технология структурного программирования
17
еле конечного числа шагов приводит к достижению поставленной
цели.
Примерами алгоритмов могут являться:
• рецепты приготовления блюд;
• пояснения, "как пройти", "как проехать";
• правило умножения целых чисел "столбиком" и т.п.
Вместе с тем, "школьная" таблица умножения не является ал
горитмом.
Способы записи алгоритмов. Один и тот же алгоритм может
быть записан (описан) различными способами. Наибольшее распро
странение в практике программирования получили [2]:
• текстуальная (словесная) запись алгоритма;
• запись алгоритма с помощью схемы (разновидность визуального
способа - формализма);
• запись алгоритма с использованием диаграммы Нэсси-
Шнейдермана (разновидность визуального формализма);
• запись алгоритма с использованием Р-схемы (разновидность ви
зуального формализма);
• запись алгоритма с помощью псевдокода^
• запись алгоритма в терминах языка программирования.
Эти способы записи алгоритмов не исключают друг друга, а
используются последовательно, на различных этапах решения зада
чи. Только три способа записи алгоритма — с использованием схемы,
диаграммы Нэсси-Шнейдермана и Р-схемы - являются альтернатив
ными на соответствующем этапе решения задачи.
Дадим краткую характеристику перечисленных выше способов
записи алгоритмов. С этой целью один и тот же алгоритм запишем
различными способами.
Текстуальная запись алгоритма. Имеется следующий алго
ритм, записанный в текстуальной форме:
1. Начало алгоритма.
2. Выполнить некоторое действие (оператор) s i .
3. Если выполнено условие "Усл1", то выполнить операторы
s2, s3 и перети к п. 4. Иначе - перейти к пп. 3.1.
3.1. Пока выполняется условие "Усл2", выполнять пп. 3.2
и 3.3. Иначе - перейти к п. 4.
3.2. Если выполнено условие "УслЗ", то выполнить опера
тор s4, иначе — выполнить оператор s5.
3.3. Выполнить оператор s6.
4. Пока выполняется условие "Усл4", выполнять оператор s7.
Иначе — перейти к п. 5.
5. Выполнить оператор s8.
6. Конец алгоритма.
18
Запись алгоритма с помощью схемы (ГОСТ 19.701 - 90, со
вместим с меэюдународным стандартом). Запись того же самого
алгоритма в виде схемы иллюстрирует рис. 3.
В случае простой схемы сравнительно несложно обеспечить ее
бесспорную наглядность. Но по мере роста сложности отображаемо
го фрагмента алгоритма (программы), его логическая структура на
чинает "тонуть" в "клубке спагетти", в который постепенно превра
щается схема алгоритма [2]. Поэтому практика использования схем
алгоритмов уже давно считается устаревшей и программисты при
меняют ее как инструмент разработки только эпизодически. Там, где
стандарты организации требуют наличия схем алгоритмов, они поч
ти неизменно рисуются после написания программы.
Запись алгоритма с помощью диаграммы Нэсси-Шнейдермана
(рис. 4). Диаграмма Нэсси-Шнейдермана была предложена в сочета
нии со структурным программированием как средство борьбы с
проблемой "клубка спагетти", присущей схемам алгоритмов. Эта
диаграмма, бесспорно, заменяет одномерное представление вложен
ных операторов двумерным (см. рис. 4). Тем не менее, по мере роста
сложности программного кода появляются проблемы отображения,
поскольку элементы диаграммы быстро становятся все меньше и
меньше. На рис. 4 приведена диаграмма Нэсси-Шнейдермана для
того же самого алгоритма, что и ранее.
Запись алгоритма с помощью Р-схемы (рис. 5). Используемая
при этом Р-технология программирования разработана в Институте
Кибернетики АН УССР. Согласно Р-технологии программа должна
быть представлена в форме нагруженного по дугам структурного
графа (Р-схемы). Р-схема состоит из подграфов, каждый из которых
имеет один вход и один выход. Вершины графа называются состоя
ниями Р-схемы. Переходы из одного состояния в другое представ
лены помеченными дугами, причем каждая дуга может быть поме
чена условием перехода и действием, выполняемым в процессе вы
полнения перехода. На рис. 5 приведена Р-схема для того же самого
алгоритма.
Запись алгоритма с помощью псевдокода. Довольно широкое
распространение получил еще один способ записи алгоритма с по
мощью псевдокода. Этот способ" записи является промежуточным и
используется перед записью алгоритма в терминах выбранного язы
ка программирования. Псевдокод представляет собой удобный для
практики промежуточный язык. Это и не естественный язык, и не
язык программирования, а их симбиоз. Псевдокод похож на язык
программирования тем, что может использовать его некоторые ин
струкции, но, с другой стороны, допускает и словесную, и формуль
ную записи там, где сразу сложно воспользоваться языком програм
мирования.
19
а) Точка входа или выхода
( )
Операционный блок
(функциональный узел)
Циклическая конструкция
Поток управления
б)
ГЦикл «Усл2»
Не «Усл2»
Нет
<^УслЗ^
Да
Цикл «Уcл4>^
Не «Усл4»
s4 s5
1
s7
s6
1
Цикл «Усл4»
\ / Цикл «Усл2»
s8
f Конец J
Рис. 3. Схема алгоритма:
а) основные обозначения; б) пример использования
а) Заголовок программы
Программа
Тело программы
Простая конструкция Оператор
Условная конструкция
Пример программы
б)
s1
Усл1
т F
Усл2
s2
УслЗ / ^
т \
S3 s4 s5
s6
Усл4
s7
s8
Рис. 4. Диаграмма Нэсси-Шнейдермана:
а) графические элементы; б) пример использования
21
рования, нашел широкое применение. Здесь также будем придержи
ваться этого подхода. По мере изучения программирования будут
рассмотрены положительные моменты структурного подхода к про
граммированию, а также основные принципы и требования струк
турного программирования.
а) Состояние Р-схемы
Действие
б)
Усл1
склгыэ Усл4
s7
Рис. 5. Р-схема:
а) графические элементы; б) пример использования
22
3. Цикл с заранее заданным числом повторений (Jor).
Этих трех элементарных конструкций, называемых базовыми
конструкциями структурного программирования, достаточно, чтобы
управлять порядком выполнения действий в любом алгоритме. От
метим также, что каждая из названных конструкций имеет только
один вход и один выход, что делает их использование очень удоб
ным.
а) («ЕСЛИ»)
(«ИНАЧЕ») Нет
-4-
ВЕТВЬ-ИНАЧЕ ВЕТВЬ-ТО
(«ВСЕ»)
б) ... («ЕСЛИ»)
(«ИНАЧЕ») Нет
ВЕТВЬ-ТО
... («ВСЕ»)
Рис. 6. Ветвление (условная конструкция):
а) общий случай; б) частный случай
/ / C++ реализация
±f( А > В )
D = Е; // ВЕТВЬ-ТО
23
// ...
else
{
С = F/ // ВЕТВЬ-ИНАЧЕ
// . ..
}
Циклы
1) С предусловием (рис. 7, 8). Пример
SUMMA = Y,I
24
do
{
SUMMA -h= I; // Эквивалентно SUMMA = SUMMA + J ,
I += 1;
}
while( I <= 20 ) ;
SUMMA = 1 + 2 + ... + 20
SUMMA:=0;
I := 1;
(«ПОКА»)
SUMMA := SUMMA + I;
I : = ! + 1; УСЛОВИЕ
(«ДЕЛАТЬ»)
ТЕЛО-ЦИКЛА Нет («ВСЕ»)
Вариант по ГОСТ
SUMMA:=0;
I := 1;
Цикл «I»
I >20
SUMMA := S U M M A + 1 ;
I := 1 + 1;
Цикл «I»
25
ные подзадачи, оформленные в виде отдельных модулей, которые в
языках Си/С-ь+ называются функциями.
Вариант по ГОСТ
SUMMA := 0;
SUMMA :=
SUMMA + I
Цикл «I»
26
Вариант по ГОСТ
S U M M A = 1 + 2 + ... + 20
SUMMA:=0;
I := 1;
SUMMA:=0;
I := 1; Цикл «I»
(«ПОВТОРЯЙ»)
27
Этим требованиям в достаточной степени удовлетворяют язы
ки Си/С++.
Языки Си/С++, как и любые другие языки программирования,
полностью определяются заданием их алфавита (словаря исходных
символов), точным описанием их синтаксиса (грамматики) и се
мантики (смысла) - "СИНТАКСИС + СЕМАНТИКА" (см. рис. 10).
/* */
\ \п \г \t \" \'
и др.
/* + - ; % » «
< > < = > = == !=
& I - '^ ! . - > { } ( ) &&
: = + = - = *= /= %= » = « =
&= 1= '^= [ ] ++ - ,
Строч
ные и Только в языке C++
:: // .* ->*
пропис и др.
ные ла
тинские
буквы Специальные символы
J..
АЛФАВИТ (ЛИТЕРЫ)
СИНТАКСИС + СЕМАНТИКА
Простота Однозначность
ЯЗЫК ПРОГРАММИРОВАНИЯ
Рис. 10. Язык программирования и его описание
28
синтаксиса языка, предложенных Д. Бэкусом и Н. Виртом в проти
вопоставлении друг другу (две параллельных колонки).
Из допустимых символов языка, указанных на рис. 10, можно
писать программу на языке Си/С++, но не в произвольном виде, а в
соответствии с синтаксисом языка. Удобными способами описания
синтаксиса языка являются следующие способы.
1. Использование металингвистических формул (предложены
Д. Бэкусом, автором языка АЛГОЛ-60).
2. Синтаксические диаграммы (предложены Н. Виртом, авто
ром языка Паскаль).
На рис. 11 приведены определения одних и тех же понятий как
через металингвистические формулы, так и через синтаксические
диаграммы.
Металингвистическая формула позволяет определить некото
рое понятие путем перечисления всех его значений. Она использует
следующие обозначения:
"::=" - знак, который читается как "это есть по определению";
<Определяемое_понятие> - пишется слева от "::=";
I - обозначает "ИЛИ";
( ) — круглые скобки, обозначают "И";
{ } — фигурные скобки, обозначают неограниченное повторение
ноль, один, два и т.д. раз, заключенной в них конструкции;
[ ] — квадратные скобки, обозначают необязательность конст
рукции, заключенной в эти скобки.
Из рис. 11 следует, что в языке Си два имени, имеющие совпа
дающие восемь первых символов, будут восприниматься одинако
выми. Вместе с тем отметим, что в интегрированных средах про
граммирования на языке C++ различимая длина идентификаторов
может задаваться программистом с помощью соответствующей оп
ции. Прописные и строчные буквы идентификаторов различимы.
Так, в частности, ALPHA и alpha - разные идентификаторы.
Использование синтаксических диаграмм поясняет правый
столбец на рис. 11. Синтаксическая диаграмма - это схема, состав
ленная из линий со стрелками, прямоугольников и овалов. В прямо
угольник заключают объект, определенный в другом месте, а в ова
лы - литеры или составные символы языка. Сопоставляя определе
ния понятий обоими способами легко понять смысл и особенности
применения синтаксических диаграмм. Из сопоставления можно за
ключить, что метод синтаксических диаграмм проще и нагляднее.
Его, в основном, и рекомендуется использовать.
29
д. Бэкус Н. В и р т
Металингвистические Синтаксические
формулы диаграммы
Прописная_буква Строчная_буква
<Прописная_буква> ::= A|B|C|...|Z
<Строчная_буква> ::= a|b|c|...|z
-Восьмеричная_цифра> ::=
Восьмеричная_цифра
( <Ненул_восьм_цифра>|0 )
1
Ненул_восьм
_цифра
I
<Ненул__дес_цифра> ::= Ненул_дес_цифра
( <Ненул_восьм_цифра>|8|9 )
X
Ненул_восьм
_цифра
Ненул_дес_
цифра
I
<Идентификатор> ::= ( <Буква>|_ ) Идентификатор
{ ( <Буква>|
<Десятичная_цифра>|_)}
Буква Буква
Ж
Эквивалентно
ДЛИНАИДЕ 8! >К. о-
Рис. 1 1. Способы описания синтаксиса языка
30
ляться некоторыми пояснениями на обычном языке или эквивалент
ными совокупностями других предложений языков Си/С++.
2.3.1. Комментарии
Комментарий
>Ci*
Печатный символ
/-^
Компилятор языка Си рассматривает эти
строки как комментарий
"-/
31
Эта часть комментария правильная
/'*• Начало этого комментария игнорируется. */
Эта строка теперь находится вне комментария! Ошибка!
Двусмысленность!
X = Y//* Это деление */Z;
Надо так:
X = Y/ /->" Это деление ^/ Z;
2.3.2. Идентификаторы
32
export extern false float
for friend goto if
Inline int long xmitable
namespa.ce new operator private
protected public register reinterpret^cast
return short signed sizeof
static static_cast struct switch
template this throw true
try typedef typeid typename
vmion unsigned using virtual
void. volatile wchar t while
2.3.4. Константы
Константы
Целая_костанта
#i Символьная константа
Строковая_константа
33
дится иметь дело с данными, представляющими комбинации битов
(получаются более короткие записи). Определение десятичной,
восьмеричной и шестнадцатеричной констант приведено на рис. 15 -
17.
Целые константы могут быть обычной длины или длинные.
Длинные целые константы оканчиваются буквой "/" или "L"
Размер целых констант обычной длины зависит от реализации,
(для шестнадцатиразрядного процессора — 2, для тридцатидвухраз
рядного — 4 байта). Длинная целая константа всегда занимает 4 бай
та. Таким образом, на тридцатидвухразрядном процессоре эквива
лентны длинная целая константа и целая константа обычной длины.
Целая_константа
Десятичная_константа
Восьмер._константа
#J Шестнад._константа —••
Десятичная_константа
Ненул ._десят._цифра
Десятичная_цифра
11 -1028 57944L
Рис. 15. Определение десятичной константы
Восьмеричная_константа
ч2>- Восьм._цифра
013 02000 0160000L
Рис. 16. Определение восьмеричной константы
34
Шестнадцатеричная_константа
\ ^ Шестнадцатерич-
М о ) ^ ная_цифра
] — • щ
Десят._цифра
35
pa мантиссы всегда равна 1, то она не хранится. Для констант с пла
вающей точкой типа double, занимающих 8 байт, под порядок и
мантиссу отводятся соответственно 11 и 52 разряда. Длина мантис
сы определяет точность числа, а длина порядка — диапазон числа.
Для констант с плавающей точкой типа long double под число отво
дится 10 байт. Также заметим, что более подробное обсуждение
внутреннего представления fe ЭВМ констант с плавающей точкой
выходит за рамки данной книги и будет рассмотрено при изучении
арифметических основ построения ЭВМ.
В языке C++, когда в конце константы с плавающей точкой от
сутствуют б у к в ы / F, /, L, константа имеет тип double (8 байтов или
64 бита с диапазоном значений ±l,7•10~^°^..±l,7 •Ю^'*^^). Если же кон
станта заканчивается б у к в о й / и л и F, то она имеет тип float, занима
ет 4 байта и диапазон значений ±3,4•10'^^..±3,4•10^^^. Аналогичным
образом, при завершении константы буквами / или L константа име
ет тип long double, занимает 10 байт с диапазоном значений
Константа_с_плавающей_точкой
F-константа
F-константа F-константа
•СУ
е Hh F-константа
<7>
-• Десятичная_константа
36
Символьные константы. Для кодирования одного символа
используется байт (восемь битов). Благодаря этому набор символов
содержит 256 символов, образующих две группы:
• печатные символы;
• непечатные символы.
Непечатным символам соответствуют специальные управляю
щие коды, которые служат для управления внешними устройствами
или для других видов управления. В качестве примера непечатного
символа назовем символ перехода к новой странице, управляющий,
например, работой принтера.
Символьная константа в языках Си/С+-ь состоит либо из одно
го печатного символа, заключенного в апострофы, либо управляю
щего кода, заключенного в апострофы. Управляющие коды пред
ставляют непечатные символы (табл. 5). Символьная константа рас
сматривается как символьный беззнаковый тип данных с диапазо
ном значений от О до 255. Константа ' \ 0 ' называется нулевым сим
волом или нулевым байтом.
Примеры: 'д:' ' Г Лп'
37
"Эта строка содержит символ табуляции \t"
"В строке указан символ,, вызывающий звуковой сигнал: \07'
"СтрокаЛп"
С т Р о к а \п \0 Байты памяти, содержащие коды от
О до 255
Нулевой
байт
Рис. 19. Размещение строки в оперативной памяти
Функция
Внутренние определения данных
Операторы
Функция
Внутренние определения данных
Операторы
38
Термин "функция" в языках Си/С+-*- охватывает понятия "под
программа", "процедура" и "функция", используемые в других язы
ках программирования. Как следует из рис. 20, Си/С++-программа
может содержать одну функцию (главная функция main) или любое
количество функций. Выполнение программы начинается с главной
функции. Приведем простой пример подобной программы.
return 0;
39
void convert (
±nt ch ) // Изображаемый символ
{
printf( "Символ 10 код 8 код 16 код \п" ) ;
// Непечатные символы имеют десятичные коды О.. 31, а
// десятичный код символа ' ' равен 32
± f ( c h < ' ' )
printf( "Управляющий (непечатный) символ: \п" ) ;
// Обратите внимание, что один и тот же символ печатается
// вначале в символьном формате %с, а затем
// соответственно в форматах десятичного %d,
// восьмеричного %о и 16-ричного %к чисел. Число
// форматов и количество следующих за управляющей
// строкой аргументов совпадают
print f( "%с %d %о %х \п",
ch, ch, ch, ch ) ;
return;
}
40
2.4. Простой ввод-вывод в языках Си/С++
41
Открытие потока. Перед выполнением операций ввода-
вывода для потока его нужно открыть. Для этой цели служит функ
ция уЬрег7(^^, описание которой имеет вид:
^include <stdlo.h>
FILE * fopen ( // Возвращает указатель на открытый
// файл
const char *path. // Указатель на имя открываемого
// файла
const cha.i: *type ); // Указатель на вид доступа к файлу
42
• стандартный ввод (предопределенный указатель stdin);
• стандартный вывод (предопределенный указатель stdout);
• стандартный вывод сообщений об ошибках (предопределенный
указатель stderr);
• стандартный дополнительный поток (предопределенный указа
тель stdaux);
• стандартная печать (предопределенный указатель stdprn).
По умолчанию stdin соответствует клавиатуре терминала,
stdout и stderr - экрану терминала, stdaux - дополнительному порту и
stdprn - печатающему устройству.
Предопределенные указатели пяти перечисленных стандарт
ных потоков можно использовать в любой функции ввода-вывода,
которая в качестве аргумента требует указатель потока.
/*
Программа-пример 1 (начало) .
43
ввод в ЯЗЫКЕ Си • написать фрагмент Си-программы^ которая
из файла " exl dat" на магнитном диске прочитает указанные ни-
же значения:
±пЬ 1г // OxFA
а. // -22
12; // 074
float f; // 1.57
long 1; // -125874
char ch. // 'Z '
str[ 20 ];// "Нам Тхань"
Написать вид читаемых данных (вид строк в файле
"exl. dat")
-^/
^Include <stdlo.h> // Для функций ввода-вывода
return О;
}
// Конец примера 1
Вид строк исходных данных в файле exl.dat:
l=OxFA 11=-22 12=074
44
Как работает функция fscanf?
Вначале слева направо просматривается управляющая строка
"...". Если очередным символом является символ "пробельной груп
пы" (пробел или '\Г' или \п'), то в исходных данных (во входном по
токе) пропускаются все подряд идущие символы пробельной груп
пы, пока не встретится другой символ.
Если в управляющей строке встретится формат, который на
чинается с символа "%", то из входного потока читается последова
тельность символов до пробельного символа. Она преобразуется в
кодовый формат в соответствии с типом формата и записывается по
адресу, заданному в соответствующем аргументе (запись &/ означа
ет адрес, по которому в оперативной памяти размещается перемен
ная /). Если до пробельного символа раньше встретится символ, не
допустимый в записи читаемого значения, то ввод по текущему
формату остановится на этом символе. Символ управляющей стро
ки, следующий за символом "%", указывает способ преобразования
символов из входного потока в кодовый формат (табл. 8).
!!! Число форматов и число аргументов в ф у н к ц и и / у с а « / о б я
зательно должно быть одинаковым.
Если в управляющей строке встретился символ, отличный от
символа пробельной группы и от символа "%", то функция fscanf
считывает очередной символ из входного потока. При несоответст
вии прочитанного символа символу, указанному в управляющей
строке, функция/л'сал?/прерывает работу и конфликтный символ ос
тается во входном потоке. В случае соответствия прочитанный сим
вол пропускается и функция продолжает работу. Отмеченная осо
бенность позволяет организовать так называемый "не слепой" ввод.
Это означает, что во входном потоке (в файле исходных данных) с
помощью лидирующих символов можно указать, к какой перемен
ной относится вводимое значение (см. текст примера выше).
Рассмотрим еще один пример, являющийся логическим про
должением предыдущего примера.
/*
Программа --пример 2 (начало) .
ВВОД В ЯЗЫКЕ Си:
1. Написать фрагмент Си-программы, которая из файла
"exl.dat" на магнитном диске прочитает указанные ниже значе-
ния:
±пЬ 1; // Ох FA
short 11/ // -22
±nt 12; // 074
float f; // 1.57
long- 1; // -125
char ch. // 'z'
45
str[ 20 ];// "Нам Тхань"
2, Написать вид читаемых данных (вид строк в файле
"exl.dat")
*/
46
^include <stdio.h> • // Для функций ввода-вывода
int main ( void ) // Возвращает 0 при успехе
{
Int i , 12;
short 11;
float f;
long 1;
char ch, str[ 20 ];
FILE *f_ln/ // Указатель на структуру со
// сведениями о файле для ввода
±nt ret code/ // Возвращаемое значение для
// fscanf
// Открываем файл exl.dat для чтения
f_ln = fopenC "exl.dat", "г" ) ;
±f( f__ln == NULL )
{
printf( "\n Файл exl.dat для чтения не открыт. ") ;
return 1;
}
return 0;
}
// Конец примера 2
Вид строк исходных данных в файле exl.dat:
l^OxFA 11=-22 12=074
47
ствии с форматом, но преобразованная величина никуда не записы
вается. "Ширина" - положительное десятичное целое, задающее
максимальное число символов при вводе. Если "ширина" избыточ
ная, то чтение, как и ранее, выполняется до пробельного символа.
Если "ширина" меньше, чем число символов до пробельного, то чи
таются и преобразуются только символы числом не более "ширина",
(см. пример 2).
Префиксами могут быть:
N - используется для печати адресов near (формат %Np);
F - используется для печати адресов far (формат УоГр);
h - для ввода коротких целых с типом short (см. пример 2);
/ - для ввода длинных целых и вещественных с типом long (см.
пример 2).
Рассмотрим еще один, более сложный, иллюстрирующий при
мер.
V7*
Программа --пример 3 (начало) .
ВВОД В ЯЗЫКЕ Си:
1. Написать фрагмент Си-программы, которая из файла
"exl.dat" на магнитном диске прочитает указанные ниже значе-
ни я:
±пЬ ±. // OxFA или 250
11, // 74
12; // 18
float f; // 1,57
long- 1; // -125874
char ch, //
str[ 20 ];// "Нам Тхань"
2. Написать вид читаемых данных (вид строк в файле
"exl.dat")
V
^include <stdlo.h> // Для функций ввода-вывода
48
printf ( "\n Файл exl.dat для чтения не открыт. " ) ;
// За крыва ем файл
retcode = fclose( f_in ) ;
±f( retcode == EOF )
{
printf( "\n Файл exl.dat не закрыт." ) ;
return 3;
}
jzebvLzm 0;
}
// Конец примера 3
Вид строк исходных данных в файле ех1.dat:
OxFA 074
22 1.57 -125874 z
Нам Тхань
V
В этом примере /1 получает значение 74, так как читается по
формату Vod (десятичный формат). Аналогично, /2 получает деся
тичное значение 18, так как читается по формату Voo (восьмеричный
формат - восьмеричный код 22 соответствует десятичному коду 18).
В заключение отметим, что функция 5са«/идентична функции
fscanf^ но вместо входного потока, заданного первым аргументом,
она по умолчанию использует предопределенный входной поток
stdin. По этой причине в вызове функции scan/ CUWCOK аргументов
начинается сразу с управляющей строки.
49
Программа-пример 4 (начало) .
ВЫВОД В ЯЗЫКЕ Си/С++:
Укажите вид строк печати в файле fl,out на магнитном диске
после выполнения приведенной ниже программы
'^7
^include <stdio.h> // Для функций ввода-вывода
return 0/
}
// Конец примера 4
50
просмотра слева направо управляющей строки "...". Если в управ
ляющей строке нет ни одного формата, то после нее аргументов то
же не будет. Символы в управляющей строке "..." могут быть трех
видов.
1. Управляющие символы (в кодовой таблице первые 32 сим
вола), примерами управляющих символов являются '\п\ \t\ '\0х7' и
т.д. Если встречается управляющий символ, то он выполняет пред
писанные ему действия. Например, '\п' вызовет переход в потоке ( в
нашем случае в файле /Lout) на следующую строку, '\^' - выполнит
печать пробелов в соответствии с используемым значением табуля
тора (табулятору может соответствовать 2 - 1 6 пробелов, обычно че
тыре или восемь) и т.д.
2. Форматы, которые начинаются с символа "%". Если встре
тился формат, то из списка аргументов берется соответствующий
ему аргумент, значение которого преобразуется в соответствии с
типом формата и выводится в поток (в нашем случае в файл fJ.out).
!!! Число форматов в управляющей строке должно быть равно
числу аргументов !!!
3. Остальные символы, которые называются печатными и вы
водятся в выходной поток в том виде, как они изображены.
Работа функции заканчивается после просмотра управляющей
строки до конца или при возникновении ошибки.
Формат имеет следующую структуру (в квадратных скобках
указаны поля формата, которые могут отсутствовать):
% [флаг] [ширина__поля_вывода ] [ . точность] [префикс] тип
51
Табл. 9. Действие флагов форматирования
Флаг Смысл Значение по умолча
нию
- Выравнивание результата по левому краю Выравнивание по пра
заданного поля вому краю
+ Вывод величины с указанием знака "+", если Знак выводится толь
величина принадлежит к типу со знаком ко для отрицательных
величин
Пробел Вывод пробела перед величиной, если это по Пробел не выводится
ложительное число со знаком
# Для форматов о, х или Х выводит перед числом Префикс в указанных
префикс 0, Ох или ОХ соответственно. Для случаях не выводится.
форматов е, Е и л и / в ы в о д и т число с десятич Десятичная точка
ной точкой. выводится, если за
Для форматов g, G выводит число с десятич ней следует цифра.
ной точкой и предотвращает усечение лишних Лишние нули
нулей. усекаются.
"Префиксы h, I, L, N, F".
Префикс "/г" (sHort) для типов d, i, о, х, А" (табл. 11) указывает
на тип аргумента short int, а для типа и - на тип аргумента unsigned
short int.
Префикс "/" для типов d, i, о, х, Jf указывает на тип аргумента
long int, для типа и - на тип аргумента unsigned long int, а для типов
е, Е, f, g, G - на тип double.
52
Табл. 11. Символы типа
\d, i int Десятичное целое со знаком
и int Десятичное целое без знака
0 int Восьмеричное целое без знака
X или int Шестнадцатеричное целое без знака с использованием
\х цифр "abcdef' или "ABCDEF"
/ float Величина со знаком вида [-]dd.dd, где d - десятичная циф
ра. Число цифр до точки определяется величиной числа, а
после - точностью.
\Е, е float Величина со знаком вида [-]d.ddddde[3HaK]ddd или [-
ld.ddddd£[3HaK]ddd
G'S float Величина со знаком, выводимая в ф о р м а т е / и л и е, Е, в
зависимости от того, что компактнее при заданной точно
сти
с int Отдельный символ i
S Строка Символы выводятся или до достижения нуль-символа,
или после вывода количества символов, заданного в поле
"точность"
п Указатель на Выводится число символов, успешно записанных к дан
целое ному м о м е т у . Эта величина присваивается переменной
inl с адресом в аргументе.
р Указатель ти Выводит адрес, на который указывает аргумент, в виде
па void far * ХХХХ : УУУУ (сегмент : смещение) с использованием
шестнадцатеричных цифр верхнего регистра
^i=''^'^^^00007^f=l. 500000Ei-02^1±=12^si=5
53
2.4.4. Упражнения для самопроверки
54
грамму, выводящую в файл результатов/ile.out следующие строки (в
них символ ^ обозначает местоположение пробела):
/•-/-254 " "-^"-^-^-"7 " " f " " ^^2547
(^^^^^1234,5600) " " (1234. 5 600''^^^^)
/Стр/^-^/м/
3.1. Имена
56
3.2. Типы данных
57
iinclude <stdio.h>
int main ( void )
{
wpr±ntf( L"%s\n%c\n", L"string", L'A' ) ;
reburn 0;
}
58
старший бит числа интерпретируется как знаковый (О - положи
тельное число, 1 — отрицательное). Спецификатор unsigned позволя
ет представлять только положительные числа, поскольку старший
разряд рассматривается как часть кода числа.
По умолчанию, все целочисленные типы считаются знаковы
ми, то есть спецификатор signed можно опускать.
Логический тип (bool). Величины логического типа могут
принимать только значения false и true, которые являются служеб
ными словами. Внутренняя форма представления значения false — О
(нуль). Любое другое значение интерпретируется как true. При пре
образовании к целому типу true имеет значение 1.
Типы с плавающей точкой (floaty double и long double).
Внутреннее представление для типов с плавающей точкой состоит
из двух частей — мантиссы и порядка. При этом величины типа float
занимают четыре байта, из которых один двоичный разряд отводит
ся под знак мантиссы, 8 разрядов под порядок и 23 под мантиссу.
Мантисса — число большее 1,0, но меньшее 2,0. Поскольку старшая
цифра мантиссы всегда равна 1, то она не хранится.
Для величин типа double, занимающих восемь байт, под поря
док и мантиссу отводятся соответственно 11 и 52 разряда. Длина
мантиссы определяет точность числа, а длина порядка — диапазон
числа.
Спецификатор long перед именем типа double указывает, что
под число отводится 10 байт.
Чтобы проверить размер памяти, выделяемой для объекта дан
ного типа, можно написать программу, использующую операцию
''sizeof {size - размер). Значением этой операции является размер
любого объекта или спецификации типа, выраженный в восьмиби
товых байтах:
59
sizeof ( unsigned long- ) ) ;
printf( "float %d \n", sizeof ( float ) ) ;
printf( "double %d \n", sizeof ( double ) ) ;
print f ( "long double %d \n", sizeof ( long double ) ),
•retujETZi 0;
}
60
• внешний;
• внешний статический;
• внутренний статический;
• автоматический;
• регистровый.
Список_опред._объектов
*н Спецификация_типа — г - н Определяемый_объект
Специф._типа
unsigned
>
•( char У
и short У
и long У
•Г float \
•Г long V^ double
>
Определяемый_объект
Идентификатор
61
Рассмотрим далее несложный иллюстрирующий пример.
Функция
Внутренние определения данных
Операторы
Файл Р2.СРР
Программа с одним модулем и двумя функциями. Программа яв
ляется примером и конкретизацией информации, представленной
на рис. 19
62
весь модуль, а время их эюизни совпадает со временем выполнения
программы. Любая функция, находящаяся в этом файле (модуле)
может иметь доступ к внешним данным. Таким образом, внешние
данные файла являются общими данными для всех функций того же
файла.
В общем случае область действия внешних данных можно рас
пространять и за пределы файла (модуля), используя служебное сло
во extern {EXTERNal - внешний).
В качестве иллюстрации перепишем программу для предыду
щего примера с использованием двух файлов.
Файл РЗ.СРР
Двухфаиловый программный проект с двумя функциями. Пример
иллюстрирует область действия и время жизни данных, имеющих
внешний класс хранения.
V
return 0;
}
Файл SAVE.CPP
Используется в программном проекте, главная функция кото-
\рого имеется в файле РЗ.СРР.
V
63
±nt ilr ±2;
-void save ( void. )
il 10; ±2 15;
jretuim/
;
в приведенном примере внешние данные /1 и /2 определены в
файле SAVE.CPP. Такое определение должно присутствовать толь
ко в одном файле многофайлового программного проекта. Чтобы
воспользоваться этими данными вне файла, где они определены (на
пример, в файле РЗ.СРР), их следует объявить с использованием
служебного слова extern. Таким образом, определение создает дан
ное (см. файл SAVE.CPP), а объявление (см. файл РЗ.СРР)- только
ссылается на данное, определенное в другом файле (рис. 23). Обра
тите внимание, что объявление внешних данных, в отличие от их
определения, может присутствовать в нескольких файлах программ
ного проекта.
ОПРЕДЕЛЕНИЕ
Определить данное типа int
, Имя нового данного
int 11;
ОБЪЯВЛЕНИЕ
Указывает, что данное определено в
другом месте (в другом файле)
Указывает тип данного
Имя существующего данного
64
^include <stdlo,h> // Для функций ввода-вывода
// Прототип функции: хотя определение этой функции находится
// в другом файле данного программного проекта (SAVE1.СРР) ,
// в данном файле прототип также нужен - он используется
// для контроля правильности вызова функции
void savel ( void. ) ;
return 0;
Файл SAVE1.CPP
Используется в программном проекте, главная функция кото
рого имеется в файле Р4.СРР
V
// Прототип функции: в принципе, в этом файле прототип не
// нужен, так как файл содержит определение этой функции.
// Мы оставляем здесь прототип только для унификации
void savel( void ) ;
return;
}
65
было бы ошибочным, так как областью действия /1 является только
файл SAVE1.CPP.
Подводя итоги сказанному, выполним некоторые обобщения.
В общем случае, программный проект является многофайловым. В
рассуждениях, относящихся к объектам с описателем класса хране
ния "внешний", будем считать, что программный проект содержит
три файла (рис. 24 а).
н
Гипотетический программный проект
fl.cpp f2.cpp fS.cpp
1 mill ^ ^ ^ ^ ^ ^ И
1 ovforn И- ^ ^ ^ H
1 extern int i1; ^ ^ H
float i 1 ;
a)
Гипотетический программный проект
fl.cpp f2.cpp fS.cpp
H
•"staticl^^
1 ctatin i9- ^ ^ ^ ^ H
1 static char ch; ^ H
{
char i2;
6)
Рис. 24. Внешние объекты:
a) с описателем класса хранения внешний;
б) с описателем класса хранения внешний статический
66
в область действия, так как в нем переопределяется объект с иден
тификатором / 1 . Область действия объекта / 1 с описателем класса
хранения extern можно сделать максимальной — все файлы про
граммного проекта. Для этого достаточно объявление объекта /1 в
файлах fZ.cpp и О.срр поместить в их начало, а вложенные блоки не
должны содержать переопределение объекта / 1 . Еще раз напомним,
что определение объекта с описателем класса хранения extern
долэюно быть только в одном файле программного проекта (лю
бом), а в остальных файлах долэюно использоваться только объяв
ление объекта.
Рис. 24 б иллюстрирует области действия объектов с описате
лем класса хранения "внешний статический". Так, областью дейст
вия объекта/является весь файл П.срр (и только он), областью дей
ствия объекта ch является залитая часть файла f2.cpp, а областью
действия объекта /2 является залитая часть файла О.срр.
Теперь можно сформулировать ряд важных уточнений, отно
сящихся к областям действия и времени жизни объектов с описате
лями класса хранения "внешний" и "внешний статический":
\. Временем эюизни внешних данных является интервал време
ни, в течение которого программа выполняется. Это верно как для
внешних, так и для внешних статических данных. Следовательно,
если внешней переменной будет присвоено значение, то оно будет
сохраняться в течение всего времени выполнения программы и не
будет утрачено между вызовами функций.
2. Областью действия внешнего данного в общем случае явля
ется вся программа за исключением влоэюенных блоков, в которых
содерэюатся переопределения данного с тем эюе именем. Вложен
ным блоком называется конструкция вида { ... }, в которой между
фигурными скобками могут находиться определения данных и опе
раторы.
3. Областью действия внешнего статического данного явля
ется файл, где это данное определено, за исключением влоэюенных в
этот файл блоков, в которых содерэюатся переопределения данного
с тем эюе именем.
Обратите особое внимание на два последних уточнения' отно
сительно областей действия внешних и внешних статических дан
ных - это очень важно!
Остальные классы хранения данных - автоматический, внут
ренний статический и регистровый - гораздо уже по области дейст
вия и, за исключением внутренних статических данных, по времени
жизни. Данные этих классов привязаны к отдельным функциям или
блокам. Поэтому перед рассмотрением областей действия и времени
67
жизни этих данных познакомимся подробнее с определениями, объ
явлениями (прототипами) функций и их вызовами.
3.5. Функции
} Имена параметров
Типы параметров
68
Объявление функции в языках Си/С++ называется прототи
пом функции. Вид его аналогичен заголовку определения функции,
за которым вместо блока функции { ... } следует символ ";". Другое
отличие списка параметров в прототипе заключается в том, что либо
разрешается указание всех имен параметров, как в заголовке опре
деления функции, либо все имена параметров можно опустить.
Давайте теперь рассмотрим иллюстрирующий пример — запи
шем прототип, определение и пример вызова функции, определяю
щей наибольшее и наименьшее значение из двух аргументов. При
проектировании функции обычно вначале составляют специфика
цию функции, которую можно рассматривать как графическую фор
му записи прототипа функции. Спецификация (прототип) функции
определяет ее интерфейсные свойства. Это означает, что на данном
этапе функция рассматривается как "черный ящик" и определяются
только ее интерфейсные свойства — входные и выходные данные.
Спецификация функции для рассматриваемого примера представле
на на рис. 26.
69
double &Min ) ;
±nt main ( void ) // Возвращает 0 при успехе
(
double al = 1.5, a2 - -17.1, Mx, Mn;
// Вызов функции без возвращаемого значения: al, а2, Мх,
// Мп - аргументы функции
MaxMln ( al, а2, Мх, Мп ) ;
prlntf( "\п al = %1д, а2 = %1д, Мх = %1д, Мп = %1д \п",
al, а2, Мх, Мп ) ;
jretujrn О;
}
return;
}
70
в нашем примере имеет место их полное соответствие, что
свидетельствует об отсутствии ошибок в вызове функции. Попутно
заметим, что в языке Си прототип функции не обязателен. При его
отсутствии компилятор выдает лишь предупреждение о невозмож
ности проверить правильность вызова такой функции, что не пре
пятствует выполнению программы. Это является недостатком языка
Си. В языке же С-ь+, напротив, прототип является обязательным и
это хорошо.
Если в файле программного проекта, где находится вызов
функции, имеется определение этой функции, причем определение
функции предшествует ее вызову, то наличие прототипа не является
обязательным. При его отсутствии для контроля правильности вызо
ва функции компилятор использует заголовок функции из определе
ния функции.
Рассмотрим процесс передачи аргументов а\ и а2 функции
MaxMin в приведенной выше программе. Что же функция MaxMin
получает в действительности - копии значений аргументов а\ и а2
или значения этих аргументов? В данном случае функция получает
копии значений аргументов al и а2. Передача функции копий зна
чений аргументов, в противоположность передаче функции значе
ний самих аргументов, называется передачей аргументов по значе
нию. При таком способе передачи значения аргументов a l и а2 ко
пируются, на время работы функции, в дополнительную область па
мяти и используется в функции в качестве параметров ArgX и Arg2.
По завершении работы функции указанная область памяти освобож
дается и может быть повторно использована. При этом сами аргу
менты al и а2 остаются неизменными (даже, если в теле функции
значения параметров будут изменены). Такой способ передачи ар
гумента в функцию, по существу, означает "упрятывание" информа
ции в функции. Следовательно, он хорош для передачи в функцию
исходных данных, которые после завершения функции должны со
хранить прежние значения.
В языке C++, в отличие от языка Си, существует и другой спо
соб передачи аргумента в функцию - передача аргумента по ссылке.
В нашем примере такими аргументами являются Мх и Мп. При пе
редаче аргументов Мх и Мп по ссылке в качестве параметров Мах и
Min используется сами аргументы Мх и Мп. По завершении работы
функции аргументы Мх и Мп останутся такими, какими они были
перед завершением функции (в нашем случае Мх получает наиболь
шее, а Мп — наименьшее значение из a l и а2). Такой способ переда
чи аргумента в функцию хорош для получения из функции ответа.
71
в рассмотренном нами примере функция не имела возвращае
мого значения. Но ведь существуют и функции, имеющие возвра
щаемое значение. Когда же их следует применять? Ответ на этот во
прос прост — если из функции получаем единственный ответ. В этом
случае удобнее его получать как значение, возвращаемое функцией.
Рассмотрим пример, иллюстрирующий такой способ получения от
вета. В качестве решаемой задачи рассмотрим более простую зада
чу, являющуюся частью только что рассмотренной задачи - запишем
прототип, определение и пример вызова функции, определяющей
наибольшее значение из двух аргументов. Спецификация соответст
вующей функции приведена на рис. 27.
double Arg1
double
double Arg1
Max
72
Мх = Max( al, a2 ) ;
printf ( " \ л al = %lg, a2 = %lg, Mx = %lg \n",
air a2, Mx ) ;
z-etuni 0;
}
// Определение функции
double Max ( // Возвращает наиб. (Argl ,Агд2)
double Arglr // Исходное данное - передается по
// значению
double Агд2 ) // Исходное данное - передается по
// значению
(
±£( Argl>Arg2 )
{
retuim Argl;
}
re bum A r g2 ;
}
73
3.6. Автоматические, регистровые
и внутренние статические данные
{ Начало блока
Операторы
Конец блока
74
Определение_внутренних_данных
^ Специф._типа Идентификатор
auto
static
register
>
Рис. 29. Определение внутренних данных
Файл Р7.СРР
Двухфайловый программный проект (файлы Р7.СРР и SAVE4.СРР)
с тремя функциями. Пример иллюстрирует время жизни и область
действия параметров функции и внутренних автоматических дан
ных
V
^include <stdio.h> // Для функций ввода-вывода
// Прототипы функций
voxd save4 ( float ) ;
float get ( void ) ;
75
/ / Определение внутренних автоматических данных. Можно и
// в такой эквивалентной форме: float mv^ pi;
auto float /nv, pi;
retvLm 0;
}
/*
Файл SAVE4.CPP
Используется в программном проекте, главная функция кото
рого имеется в файле Р7.СРР
// Прототипы функций
void save4( float ) ;
float get( void ) ;
static float fv;
return fv;
}
/*
Файл Р8.СРР
Однофайловый программный проект с одной главной функцией.
Пример иллюстрирует переопределение данных во вложенных бло
ках
*/
76
^include <stdio.h> // Для функций ввода-вывода
int main ( void. ) // Возвращает О при успехе
{
// Определение внутренних автоматических переменных
int counter^ // Действует в блоке main и во
// вложенном блоке while
i; // Действует в блоке main и не
// действует во вложенном блоке
// while
counter = О; 1 = 10;
while ( counter < i )
{
// Переопределение внутренней автоматической
// переменной во вложенном блоке - она
// располагается в другой области памяти
// и действует в блоке while
int i;
i ^ 0; counter++;
printf( "\n counter ^ %d i = %d", counter, i ) /
}
print f ( "\n\n counter = %ci i = %d \л", counter, i ) ;
retuizn 0;
}
counter = 1
i =О
counter ^ 2
i =О
counter = 3
i =О
counter = 4
i == О
counter = 5
i =О
counter = 6
i = 0
counter = 7
i ^ 0
counter = 8
i = 0
counter =91=0
counter = 10 i = 0
counter^ = 10 i = 10
/ / Определение функции
void, save ( float mv )
{
// Определение внутреннего статического данного с его
// инициализацией при трансляции
static int counter == 0;
11
counter++,
return/
}
78
Данные с классами хранения extern и static инициализируются
однократно в момент компиляции. Автоматические и регистровые
данные инициализируются в процессе выполнения программы при
каждом входе в блок, в котором они определены.
^include <stdlo.h>
// Прототипы функций
int next ( void ) ;
tub reset ( void ) ;
int last ( void ) ;
int nw ( ±nb ) ;
int i = 1;
jretujrn 0;
int nw ( Int 1 )
79
{
static int j = 5;
rGtuJcn 1 =j += 1;
/ k k k k k k k k k k k k - k k k k k k - k k k k k фз^р[Л 3 k k k k k k k k k k k k k k k k k k k k k k k k k k k k /
extern Int i/
;
2. Что напечатает следующая программа?
^include <std±o.h>
// Прото типы функций
int next ( int );
int reset ( void. ) ;
int last ( int ) ;
int i = 2;
i = reset ( );
£or( j = 1; j <= 2; j++ )
J
printf( "\ni = %d j = %d\n"r i, j );
print f( "next ( i ) = %d\n", next ( i ) );
printf( "last( i ) = %d\n'\ last ( i ) );
}
return 0;
}
/ / k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k - k k k k k k k k k k k k k - k k k k k k k k k
80
return j = i--/
i
Как уже указывалось, ответы для этих упражнений можно про
верить в разд. 18.
3.9.1. Массивы
81
По этой причине массив kafjname может хранить в виде стро
ки название кафедры, состоящее не более чем из 14 символов!
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
kaf_name | к а Ф е Д Р а А В Т \0 ? ? ?
Рис. 30. Инициализация массива
Файл P9.CPP
Двухфайловый программный проект (файлы Р9.СРР и STACK.СРР)
с тремя функциями: главной функцией и функциями занесения в
стек и извлечения из стека. Стек организован на базе внешнего
82
статического массива, состоящего из элементов с типом float.
Стек доступен в функциях не за счет передачи через список па
раметров^ а за счет своей области действия и времени жизни.
return 0;
}
Файл STACK.CPP
Содержит функции для занесения элемента и извлечения эле
мента из стека. Используется в программном проекте, главная
функция которого имеется в файле Р9.СРР
V
^include <stdio.h> // Для функций ввода-вывода
// Прототипы функций (в принципе - прототипы здесь не нужны,
// мы оставляем их в этом файле только для унификации
void push ( float );
void pop ( float &, int & ) ;
83
// файле, время жизни - программа
static float s[ N ] ;
// Указатель вершины стека: вначале стек пуст
static unsigned, int
top;
// Занесение в стек
void push(
float V ) // Заносимое значение
{
if( top < N )
{
s[ top++ ] = V/
}
else
{
print f ( "\n Стек полон - занесение не выполнено" ) ;
}
return;
}
// Извлечение из стека
void pop (
// Извлеченное значение - передается по ссылке
float &out,
// О - извлечение не выполнено (стек пуст)
int &f )
{
if( top > О )
{
out = s[ --top ]; f = 1;
}
else
(
f = 0;
}
return;
84
знака, так как индексы элементов массива начинаются со значения
индекса, равного нулю.
Наряду с такими массивами можно использовать двухмерные
массивы и массивы большей размерности. Рассмотрим еще один ил
люстрирующий пример.
Файл Р10.СРР
Однофайловый программный проект с одной главной функцией.
Пример иллюстрирует работу с двумерными массивами из элемен
тов символьного типа
V
return 0;
85
6, Кафедра САПР
7. Кафедра СУД
6 6 К а Ф е д р а С А п р \0
7 7 К а Ф е д р а с У Д \0 ?
arr_kaf__name [ 7 ] [ О ] arr_kaf_name [ 7 ][
arr_kaf__name [ 7 ][ 15
Файл Р11,СРР
Одно файловый программный проект с двумя функциями. Пример
иллюстрирует передачу массива в функцию
86
^define N8 // Строковый размер массива
idefine М 16 // Столбцовый размер массива
// Определение двухмерного символьного массива с
// инициализацией: N строк^ каждая строка из М символов
static char arr_kaf_name [ N ] [ М] =
{
"Состав ФТК:", // Инициализация первой строки
"1 . Кафедра АВТ",
"2. Кафедра ТК",
"3, Кафедра САУ",
"4. Кафедра МУС",
"5. Кафедра ИИТ",
"6. Кафедра САПР",
"7. Кафедра СУД"
);
// Прототип функции: обратите внимание как записывается
// параметр - двумерный массив. Здесь прототип также не
// обязателен, так как файл содержит определение функции
void display ( cha.r arr_kaf_name [ N] [ М ] ) ;
return 0;
}
return;
}
87
// Определение внешнего статического массива с
// инициализацией. Массив содержит элементы целого типа:
// N строк, каждая строка из М элементов
stable ±zit а[ 3 ] [ 4 ] =
{
{ 1 , 3 , 5 , 1 } , // Инициализация первой строки
{ 2, 4, 6, 8 } ,
{ 3, 5, 7, 9 }
} ;
3.9.4. Структуры
3timet STUDENT__INFO
88
// Фа культет
char fak_name[ 30 ];
char fio[ 30 ];// ФИО
// Номер группы
char group_name[ 7 ];
char date[ 9 ];// Дата поступления студента в ВУЗ
float stip/ // Размер стипендии
};
ИЛИ
89
char fio[ 30 ];// ФИО
// Номер группы
char group_name[ 7 ];
char date[ 9 ];// Дата поступления студента в ВУЗ
float stip; // Размер стипендии
} current; // Имя структурного объекта
90
struct STUDENT_INFO // Тэг структуры
{
// Факультет
сЬяг fak_name[ 30 ];
char fio[ 30 ];// ФИО
// Номер группы
cbatr group__name [ 7 ];
char date[ 9 ];// Дата поступления студента в ВУЗ
float stip; // Размер стипендии
} current = // Имя структурного объекта
{
"ФТК",
"Иванов И. И. " ,
"1081/4",
"01-09-97" ,
100 000,Of
} ;
Файл Р14.СРР
Однофайловый программный проект с двумя функциями. Пример
иллюстрирует передачу структуры в функцию по значению
(следует заметить, что этот способ не рекомендуется)
V
iinclude <stdlo.h> // Для функций ввода-вывода
/*
Объявление структурного типа, определение и инициализация
структурированного объекта
V
91
char fiol 20 ];// ФИО
// Группа
char group^^name [ 7 ];
char date[ 9 ];// Дата поступления в университет
float stlp; // Размер стипендии
current = // Определение объекта
{
"ФТК:",
"Иванов И. И.
"1081/4",
"01-09-97",
100000,Of
};
retvLrn О;
}
return;
/*
Файл P15.CPP
Однофайловый программный проект с двумя функциями. Пример
иллюстрирует передачу структуры в функцию по ссылке. Такой
способ передачи структуры в функцию рекомендуется в качестве
основного
*/
92
Объявление структурного типа, определение и инициализация
структурного объекта
V
struct STUDENT INFO // Сведения о студенте
{
// Факультет
cha,r fak__name[ 30 ];
char fio[ 20 ];// ФИО
// Группа
сЬа.г group__name [ 7 ];
char date[ 9 ];// Дата поступления в университет
float stip; // Размер стипендии
current = // Определение объекта
{
"ФТК:",
"Иванов И. И.
"1081/4",
"01-09-97",
100000,Of
};
return 0;
}
return;
}
3.9.6 Упражнения для самопроверки
93
число_ 1 число_2
4.2. Операторы-выражения
95
Оператор
L
A
^V ' У ^
'
L
^ Выражение
^
->( return V ^
< < >
Выражение
4'>
->^ break
^
->r continue ^
- ^ goto J ь Идентификатор
^
• jf
switch
^
Блок
^ do-while
while
' ^
for
•
u i i c ; | ^ a 1 Kjyj
96
фигурные скобки. Повторно отметим, что в языке C + + определе
ния объектов и операторы могут чередоваться, но определения
объектов долэюны всегда предшествовать их использованию.
Обратите также внимание на то, что в описании синтаксиса
языков Си/С++ всюду, где указан "оператор", в качестве последнего
можно использовать блок операторов.
4.6. Оператор if
97
"оператора_2" управление передается следующему за if оператору
программы.
!=0
= 0
•Г else V^ Оператор_2
98
char ch;
±f( ch -= 'a ' ;
index = 1;
else ±f( ch == 'b ' ;
index =2;
dovble а = 77. 7;
int i - 5;
if(i<3)
±f( i ^= 2 )
a = 1.1;
else
a = 2.2;
// Здесь имеем a = 7 7 . 7
/ / Другой пример
double a = 77. 7/
int i = 5;
if(i<3)
{
±f( i -= 2 )
a = 1.1;
I
else
a = 2.2;
// Здесь имеем a =2.2
99
в заключение отметим, что при ветвлении на два направления
достаточно использовать один оператор if. При ветвлении на три и
более направлений можно использовать вложенные операторы if В
последнем случае возможна и другая альтернатива, которая рас
сматривается далее.
switch ( ch )
{
case 'a':
1ndex = 1; break;
case 'Ь':
index = 2; break/
case 'у':
Index = 25; break;
case 'z ':
index =^ 26; break;
default:
index = 0;
}
100
управление передается оператору, следующему за default. Если ни
одного совпадения не произошло, а служебное слово default отсут
ствует, то управление передается оператору, непосредственно сле
дующему за последней фигурной скобкой оператора switch.
OnepaTop_switch
Оператор case
Ц^ default y
101
switch( выражение ) // Выражение не может быть
{ // вещественного типа
case конст._выр,_1:
Операторы
break; —
case конст._выр,_2:
Операторы
break;
Оператор while
-Н(^ while
while ")-КГО~Л Выражение Оператор
102
Обычно тело цикла представляет собой блок операторов. Это
позволяет обеспечить циклическое выполнение группы операторов.
Но в частном случае, если "выражение" имеет нулевое значение, те
ло цикла не будет выполняться ни разу. Это является отличительной
особенностью циклов с предусловием.
Рассмотрим и проанализируем ряд специальных примеров.
/ / пример 1
while ( fun( ) == 1 ) ; // В качестве тела цикла использован
// пустой оператор
bxrea-k ;
103
while( выражение) while( выражение )
{ {
break; continue;
} }
Оператор do-while
- ^ / ddo
o j - H Оператор V-U while V ^ / ( V H Выражение Н->Г ) \>( \ )—•
do do
{ {
Ьгяак" continue*
} }
while( выражение ); while( выражение);
104
Остальные особенности цикла while (использование пустого
оператора, "бесконечный" цикл, вложенность циклов и т.п.) в рав
ной степени относятся и к циклу do-while.
Оператор for
>j Выражение_1
о Выражение_2
Выражен ие_3
•о Оператор
/ / пример
char namel 20 ] = "Кафедра АВТ"^
kaf__name [ 20 ];
int 1;
// Копирование пате в kaf_name с использованием цикла for
fori i = 0; пате[ i ] != '\0'; i ++ )
{
kaf_name [ i ] = name [ i ]/
}
kaf name[ i ] = '\0' /
105
// Копирование name в kaf_name с использованием цикла while
// Инициализация управляющей переменной цикла
1 - О/
while ( пате[ i ] != '\0' )
{
kaf__name[ i ] = name [ i ];
// Модификация управляющей переменной цикла
±++;
}
kaf_name[ i ] == '\0'/
106
for( выр1; выр2; вырЗ ) for( выр1; выр2; вырЗ )
{ {
break; continue;
} Передача управления на
«выражениеЗ» - см.
эквивалентное представление
Рис. 40. Использование операторов break и continue в цикле/Ьг
{
float а[ N ]/ // Массив для печати
107
если заранее известно число повторений цикла. В остальных случа
ях используются циклы while и do-while, причем цикл do-while сле
дует применять, если требуется тело цикла выполнить не менее од
ного раза.
108
if( с =^ 1 ) а + + / else ±f( с 2 ) а-
else ±£( с =^ 3 ) а += 1/
while( выражение )
{
while( выражение )
{
(oop_end:
<
Рис. 41. Использование оператора go/^о для выхода
из гнезда циклов
Нет
<^а <= Ь ^ г:=3; i к
1 Да
к:=п;
г:= 1;
109
5. Пусть определен массив
±nt а[ 25 ];
111
продолжение табл. 16
Наименование Знаки операций Порядок
операций выпол
нения
Унарные ++ - постфиксный инкремент: lvalue++ Нет
— постфиксный декремент: lvalue-
new - динамически создать объект (выделить
динамическую память): new type или
new type( списоквыражений)
delete — уничтожить объект (освободить
динамическую память): delete указа-
гел ь н а о б ъ е к т
delete[ ] - уничтожить массив объектов:
delete указательнамассивобъектов
++ - префиксный инкремент: -ь+lvalue
— префиксный декремент: —lvalue
* - разадресация (разименование): * выражение
& - получение адреса объекта: & lvalue
+ - унарный плюс: + выражение
— - унарный минус: - выражение
! - логическое отрицание (not): !выражение
'- - поразрядное дополнение: -выражение
sizeof - размер в байтах: 812еоГ(объект)
или sizeof(THn)
typeidO - идентификация типа времени
выполнения: typeid(type)
(type) - приведение типа: (type)выpaжeниe -
приведение типа выражения к типу в скобках
(старый стиль), выполняется справа-налево
COnst_cast — константное преобразование типа:
const_cast<type>(выpaжeниe)
dynamic_cast - преобразование типа с проверкой
во время выполнения:
dynamiccast <1уре>(выражение)
reinterpret__cast - преобразование типа без
проверки: reinteфret_cast<type>(выpaжeниe)
static__cast — преобразование типа с проверкой во
время компиляции: static cast<type>(выpaжeниe)
.* - выбор члена класса посредством объекта:
объект. *указатель_на_член_класса, выполняется
слева-направо
->* - выбор члена класса посредством указателя на
объект:
указатель на объект ->* указатель на член класса,
выполняется слева-направо
Мультипликативные * - умножение: выражение * выражение Слева -
бинарные / - деление: выражение / выражение | направо
% - остаток от деления (деление по модулю):
выражение % выражение
112
Продолжение табл. 16
1 Наименование Знаки операций Порядок
операций выпол
нения
Отношения бинарные < - меньше: выражение < выражение Слева —
> - больше: выражение > выражение направо
<= - меньше или равно: выражение <= выражение
>= - больше или равно: выражение >= выражение
Отношения бинарные == - равно: выражение == выражение Слева —
!= - не равно: выражение != выражение направо |
Поразрядная "И" & - поразрядное умножение: Слева -
бинарная выражение & выражение направо
Поразрядная ^ - выражение '^ выражение Слева -
"ИСКЛЮЧАЮЩЕЕ направо
ИЛИ" бинарная
Поразрядная 1 - выражение | выражение Слева -
"ВКЛЮЧАЮЩЕЕ направо
ИЛИ" бинарная
Логическая "И" && - логическое умножение: Слева -
бинарная(and) выражение && выражение направо
1 Логическая "ИЛИ" ii - логическое умножение: выражение || выражение Слева -
бинарная (or) направо
Условная тернарР1ая ?: - выражение ? выражение : выражение Справа-
налево
Простое присваивание = - lvalue = выражение Справа —
налево
Совмещенное *= - выражение *= выражение Справа —
присваивание /= - выражение /= выражение налево
%= - выражение %= выражение
+= - выражение += выражение
-= - выражение -= выражение
« = - выражение « = выражение
» = - выражение » = выражение
&= - выражение &= выражение
1= - выражение |= выражение
^= - выражение ^= выражение
Генерация исключения throw - throw выражение Нет
Запятая , - выражение , выражение Слева -
(последовательность) направо |
113
• ссылка на элемент структуры .;
• ссылка на элемент структуры с помощью указателя ->.
Особое место в этой группе занимает операция "()", служащая
для управления порядком выполнения операций в выражении. Объ
ясняется это тем, что данная операция, как и другие операции ссыл
ки, имеет наивысший приоритет.
а г г эквивалентно <&агг/" О ]
114
и одно сложение. Поэтому работа с массивом по сравнению со c i ^ -
лярными объектами происходит существенно медленнее и это сле
дует иметь в виду.
Получим функцию индексации для двумерного массива. Дву
мерный массив (в математике его называют матрицей) располагает
ся в оперативной памяти по строкам - сначала располагаются эле
менты первой строки матрицы в порядке возрастания индексов и
адресов оперативной памяти, затем - второй строки и т.д.
#define N10 // Строчный размер матрицы (массива)
#define М 9 // Столбцовый размер массива
// Определение двумерного массива
short ±nt two_arr[ N ][ М ]/
115
STUDENT__INFO s_data, // Структура
*ps_data = &s__data;
// Указатель на эту же структуру
... s_data.stlp /* эквивалентно */ ps__data->stlp
116
а = arr [ X ]; /* эквивалентны */ а = arr [ к + + ];
к = к + 1;
При использовании "++" и "—" тип результирующего значения
тот же, что и тип операнда, над которым выполняется операция.
117
5.3. Бинарные операции
З д е с ь Ыпор - о д н а из б и н а р н ы х о п е р а ц и й , п р и в е д е н н ы х в т а б л .
18.
Т а б л . 18. Б и н а р н ы е о п е р а ц и и
, /, % Умножение, деление, взятие остатка от деления целого на целое
Сложение, вычитание
Операции сдвига (обсуждаются в разделе "Поля битов и побитовые
» операции")
Больше, меньше
Больше или равно
Меньше или равно
Равно
Не равно
&, м Поразрядные логические операции (обсуждаются в разделе "Поля битов
и побитовые операции")
&&, Логическое И, логическое ИЛИ
Примеры. 12 % 6 д а е т О, 13 % 6 д а е т 1, 3 % 6 д а е т 3 и т.д.
Если арифметическая операция или операция отношения со
д е р ж и т о п е р а н д ы р а з л и ч н ы х т и п о в , то к о м п и л я т о р в ы п о л н я е т а в т о
м а т и ч е с к о е п р е о б р а з о в а н и е их т и п о в , если з а м е н а т и п о в я в н о не
указана. Такое преобразование производится путем "продвижения"
значений операндов к "наибольшему" типу:
118
Алгоритм выполнения очередной бинарной арифметической
операции ("*", "/", "+", "-") или операции отношения ("<", ">", ">=",
"<=", "==", "!=") состоит в следующем.
1. Если один операнд имеет тип long double, то и второй опе
ранд преобразуется к типу long double и выполняется операция.
Иначе производится переход к п. 2.
2. Все операнды с типом float преобразуются к типу double и
производится переход к п. 3.
3. Если один операнд имеет тип double, то и второй операнд
преобразуется к этому же типу и выполняется операция. Иначе вы
полняется п. 4.
4. Если один операнд имеет тип unsigned long int, то и второй
операнд преобразуется к этому же типу и выполняется операция.
Иначе выполняется п. 5.
5. Если один операнд имеет тип long int, то и второй операнд
преобразуется к этому же типу и выполняется операция. Иначе вы
полняется п. 6.
6. Если один операнд имеет тип unsigned int, то и второй опе
ранд преобразуется к этому же типу и выполняется операция. Иначе
выполняется п. 7.
7. Если операнды имеют тип unsigned char и/или unsigned short
int, то они преобразуются к типу unsigned int и выполняется опера
ция. Иначе выполняется п. 8.
8. Если операнды имеют тип char и/или short int, то они пре
образуются к типу int и выполняется операция.
Далее по такому же алгоритму выполняется следующая из пе
речисленных выше арифметических операций и операций отноше
ний.
Результат арифметической операции имеет такой же тип, как и
тип операндов после преобразования. Результат операции отноше
ния всегда имеет тип int (О - "ложь", "нет" и 1 - "истина", "да").
И еще раз повторим важное замечание. В вызовах функций ар
гументы с типом float автоматически преобразуются к типу double, а
аргументы с типом char преобразуются к типу int.
119
Как указывалось выше, результатом вычисления отношения
может быть или ненулевое целое значение ("истина") или нулевое
значение ("ложь"). Таким образом, после выполнения присваивания
X = count < slzeof( la гray ) / 2
// Прототип
±nt lf( ±nt pi, ±nt p2 )
// Определение функции
±nt If( // Возвращает значение функции
±nt // Параметр функции
±nt P2 ) // Параметр функции
(
±f( ( !pl ) && p2 )
120
retuxm 1,
jretux-n 0;
121
в приведенном примере вначале будет выполнено присваива
ние min = О, в результате которого ''min'' станет равно нулю, а затем
это же значение будет присвоено "max",
Операцию присваивания можно записывать в сокращенной
форме, что широко используется:
Обычна я ф орма Сокращенная форма
count = count + 2; count += 2;
offset = offset / 2; offset /= 2;
s = s * ( f-h2 ) ; s *= f + 2;
a[ i+2 ] = a[ i+2 ] - 10; a[ i+2 ] -= 10;
123
дает точно такой же результат, как и предыдущий вызов.
iarray = выражение/
// Ошибка: имя массива является указателем на место
// расположения массива в памяти^ ему нельзя присваивать
// новое значение
"Это строка \п" // Значением строки является
// указатель на первый символ
// строки^ т.е. адрес символа "Э"
// в памяти
X = 2;
рх = &х; // Теперь указателем рх пользоваться
// можно: *рх равно 2
*рх = 3; // Теперь *рх равно х равно 3
return 0;
}
Единственное ограничение применения операции взятия адре
са & состоит в том, что ее нельзя применить к объекту с классом
хранения register, а также к полям битов (поля битов обсудим позже
в разделе "Поля битов и битовые операции").
Из приведенного выше примера следует, что к переменной-
указателю, как только ей присвоен допустимый адрес, можно при
менить операцию разадресации, обозначаемую "*". Приоритет и по
рядок выполнения этих операций также указаны выше в табл. 16.
/ / Пример без разадресации
int main ( void )
{
int X, у /
X = 2; у = x;
return 0;
}
// Эквивалентный пример с разадресацией
int main( void )
(
int X, у г *рх;
X = 2; рх = &х; у = *рх;
return О;
}
// Здесь *рх означает извлечь значение^ находящееся по адресу
// рх: косвенная адресация
Из приведенного выше первого примера следует также, что
операцию разадресации можно использовать и в левой части выра
жения присваивания.
Инициализация указателей. Указатели, как и объекты других
типов, можно определять, совмещая определение с инициализацией.
Приведем несколько примеров такого рода.
/ / Использование указателей в инициализирующих выражениях
// (рх - адрес х)
Lnt X, *рх = &х;
char line [ 80 ], "upline = line;
// pline - адрес line[ 0 ]
int *page = 0xB800;
// Указатель на начало видеобуфера
// цветного дисплея
125
Арифметические операции над указателями. В связи с
арифметическими операциями над указателями рассмотрим не
сколько примеров.
Пример 1.
±пЬ i [ 6 ] г *pi = i /
/ / i и pi - адрес i [ О ]
Пример 2.
#define N 3 // Строчный размер массива
^define М 4 // Столбцовый размер массива
int а[ N ] [ М ] , *ра == а ;
126
Пример 3.
/ / Программа добавляет строку s к концу строки t
return 0;
}
127
Пример.
// Сравнение указателей: копирование одной строки в другую
jretujrn О;
// Указатель на структуру
STUDENT_INFO s_data, // Определение структуры
sl_data уг // Определение еще одной структуры
*ps_data; // Указатель на структуру данного
// типа
ps_data = &s_data; // Указатель на s_data: адрес
// первого байта s_data
128
// эквивалентны
... s__data . stip ... ps_data->stip ...
Приоритеты и порядок выполнения операций "." и "->"
приведены выше в табл. 16.
/ / Определение адреса элемента структуры: обе приведенные
// ниже формы эквивалентны
... &s__data , stip ... &ps_data->stip ...
/ / Операция присваивания над структурами: оба операнда должны
// быть объектами с одинаковыми типами (тегами)
sl_data = s_data;
Файл STRCAT.CPP
Добавление строки с указателем ps к концу строки с указа
телем pt
V
/ / Определение функции
void Streat(
char *pt, // Указатель на строку-приемник
char *ps ) // Указатель на строку-источник
{
while ( *pt ) pt + + /
// Здесь pt - адрес завершающего символа строки с
129
// указателем pt, т.е. адрес '\0'
// Посимвольно копируем строку с указателем ps в "хвост"
// строки с указателем pt, пока не будет скопирован
// нуль -символ
return;
/^
Файл P24.CPP
Главная функция, использующая функцию strcat^
*/
^Include <stdio.h> // Для функций ввода-вывода
// Прототип
void strcat ( char *, char * ) ;
±Tib main ( void. ) // Возвращает 0 при успехе
{
// Строка-приемник
char t[ 24 ] = "Персональная ";
strcat ( t , "ЭВМ IBM PC" ) ;
printf( "\n Конкатенация строк: %s", t ) ;
return 0;
}
130
Рассмотрим еще несколько примеров, целью которых является
показать необходимость использования в списке параметров функ
ций языка Си адресов объектов для получения из функций результа
тов (сказанное относится только к языку Си).
/*
Файл Р25.СРР
Однофайловый программный проект с двумя функциями. Пример
иллюстрирует возможность появления ошибки из-за отсутствия в
Си передачи в функцию параметра по адресу (ссылке)
V
^include <stdio.h> // Для функций ввода-вывода
геЬлт О;
}
Файл Р26,СРР
Однофайловый программный проект с двумя функциями. Пример
иллюстрирует использование в функции Си параметра-адреса
объекта для получения из нее измененного значения оаъекта
131
^include <stdio.h> // Для функций ввода-вывода
// Прототип
void swap ( int *, int *) ;
int main ( void. ) // Возвращает 0 при успехе
{
int X = 10, у = 20;
swap ( &x, &y ) ; // В функцию передаются указатели
print f ( "\n X = %d, у = %d"r ^r У )f
// Теперь будет напечатано x = 20, у = 10
re turn Of
}
Файл Р2 7.СРР
Однофайловый программный проект с двумя функциями. Пример
иллюстрирует использование в функции языка C++ параметров,
передаваемых по ссылке, для получения из нее измененных зна
чений объектов
_V
^include <stdio.h> // Для функций ввода-вывода
// Прототип
void swap ( int &, int & ) ;
int main ( void ) // Возвращает 0 при успехе
{
int X = 10, у = 20;
swap ( X, у ) ;
132
printf( "\n X = %d, у = %d"r K, у ) ;
// Будет напечатано x = 20, у = 10
retujcn 0;
}
Файл Р28.СРР
Одно файловый программный проект с двумя функциями. Пример
иллюстрирует сцепление нескольких строк с помощью функции ко
пирования строки, использующей указатели
V
133
return О;
}
/ • "
return pt;
134
"ФТК" ,
"Иванов И. И.
"1081/3",
"01-09-97"
}.
135
предположим, что имеется программа prog.exe, которая должна чи
тать исходные данные из файла prog.dat и писать результаты своей
работы в файл prog.res.
Тогда программу можно запустить из среды MS DOS с помо
щью следующей командной строки:
prog.ехе prog,dat prog.res [Enter]
Файл Р29.СРР
Однофайловый программный проект с одной функцией. Пример
иллюстрирует передачу в программу аргументов командной стро
ки. Программа печатает количество слов в командной строке и
сами слова
*/
136
6.8. Замена типов указателей
Файл РЗО.СРР
Однофайловый программный проект с одной функцией. Пример
иллюстрирует замену типов указателей и производит побайтовое
копирование одной структуры в другую
V
137
Операция замены типа {char *) указывает компилятору, что
перед применением надо интерпретировать адрес структуры &s\ как
указатель на символ.
Рассмотрим еще один пример, демонстрирующий мощь и изя
щество указателей.
/*
Файл Р31.СРР
Однофаиловыи программный проект с одной функцией. Пример
иллюстрирует "хитросплетение ссылок". Что напечатает данная
программа?
138
а) б;
ГР| ПГ"
ГТ"
i О
4и
[т1 F R "
Гз" \0 N
"Т1 ГТ]
\0 \0 \0
*( *( ++СРР ) ) ( *( - ( *( ++СРР ) ) ) ) + 3
1 1
2 2
Операции одинакового
приоритера, выполняются 4
справа налево. 5-
Будет напечатано: Операции одинакового
POINT приоритета, выпол
няются справа налево.
Будет напечатано:
POINTER
Рис. 43. "Хитросплетение ссылок"
Файл Р32.СРР
Однофайловый программный проект с одной функцией. Пример
иллюстрирует работу с массивом с использованием указателей.
Что напечатает данная программа?
V 1
int а[ 3 ] [ 3 ] == { { 1, 2, 3 } ,
( 4, 5, 6 Ь
{ 7 , 8 , 9 } } ,
139
int main ( void. ) // Возвращает 0 при успехе
{
£оз:( ±nt i = О; i < 2; i + ч- )
{
pr±ntf( "\n%d %d %d"r a[i][2-i], *a[i],
*(*(a + i)+±) ) ;
jretux-22 0;
d)
'W
срр
ср
1 ^
с
н^^^^
/
/ ( < | \ \г_
"pl гт~ Е N Р F
" Q | гг N Е О 1
"Tl ГЦ Т Р 1 R
\0 " N I ГЦ Е \0 N S
T l ГТ" R Т Т
\0 1 \о 1 \о 1
( *( срр[ -2 ] ) ) + 3 срр[ -1 ] ) [ -1 ] ) + 1
((
I I
1 1 1
1
2
3 г.
Приоритет [ ] выше, 1
чем *. 3 ого
Будет напечатано с Операции одинаков
приоритета, выпол
учетом предыдущей
няются справа налево.
печати: Будет напечатано с
POINTER ST
учетом предыдущей
печати:
Р OIN"ГЕК STEF3
Прод. рис. 43
140
эквивалентно &а[/][/]. Следовательно, *(*(а+/)-н/) эквивалентно
a[im.
Таким образом, программа напечатает:
3 11
5 4 5
fojci p = a + 5; p >= a + 1; p -= 2 )
printf ( " %3d", *p ) ;
printf ( "\л" ; /
return 0;
pp = pp + 4;
printf ( "pp-p =%3d *pp-a =%3d **pp =%3d\n", pp-p,
*pp-a, **pp ) ;
*pp-~;
printf ( "pp-p =%3d *pp-a =%3d **pp =%3d\n", pp-p,
*pp-a, **pp ) ;
*++pp;
printf( "pp-p ==%3d *pp-a =%3d **pp =%3d\n", pp-p,
*pp-a, **pp );
141
--^рр;
printf( "рр-р =%3d *рр-а =%3d **рр =%3d\n", рр-р,
*рр-а, **рр ) ;
j c e t u i m О;
)
Интенсивность символа
Признак мерцания
Рис. 44. Представление символа экрана в видеопамяти
143
вый же элемент структуры, не являющийся полем битов, но сле
дующий за битовым полем, размещается со следующего слова памя
ти из 16 битов. При этом в предыдущем слове часть битов может
оказаться неиспользованной.
/ / Доступ к отдельным полям битов в структуре и присваивание
// им значений
sd.blink = 1; // Установить мерцание
// Установить цвет символа - три единичных бита
sd,forgrd = 1;
sd.ch = 'А'; // Буква «А» прописная
144
ные типы, то они перед выполнением операции приводятся к одина
ковому (старшему) типу по правилам, аналогичным указанным в
подразд. 5.3. С помощью операции "&" удобно проверять и обну
лять биты, операции "|" - устанавливать биты, а операции "^" - про
верять несовпадение битов.
Л"
Табл. 22. Определение операций " & " , "|", '
Бит левого Бит правого Ы&Ьг Ы\Ьг Ы" Ьг
операнда (Ь/) операнда (Ьг)
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0 i
/*
Файл РЗЗ. СРР
Однофаиловый программный проект с одной функцие й. Пример
иляюстрируе т действие операции сдвига влево (ВС+ + 3. 1)
V
145
print f( "\n%dr %d, %d, %d, %d, %d, %d, %d", x « I , x«2,
x«3, x«0, x«30r х«-327б8, x«-32161,
x«-32766 ) ;
те turn G;
}
// Будет напечатано 2, 4 , 8 , 1 , О, 1, 2, 4
Файл Р34.СРР
Однофайловый программный проект с одной функцией. Пример
иллюстрирует действие операции сдвига вправо (ВС++ 3.1)
return 0;
146
}
// Рассмотрим пример
char с, d;
с = с & 'OxlF'; // Обнулить (маскировать) старший
// бит
с = с & (- ' 0x7F) '; // Маскировать все биты, кроме
// старшего
W W W W NULL
I start
Рис. 45. Однонаправленный линейный список
stxract EL ЕМ
{
char ch; // Основное поле: символ
ELEM *neKt; // Указатель на следующий элемент
} ;
148
в языках Си/С++ имеется возможность динамического разме
щения некоторого объекта в оперативной памяти (функция malloc{ )
и др. в библиотеке языка Си, оператор new языка СН-+) или освобо
ждения занятой ранее динамической памяти (функция free{ ) в биб
лиотеке языка Си, оператор delete языка C++):
/*
Пример освобождения динамической памяти^ занятой элементом
линейного списка. Среда языка Си
• " /
149
±f( ре != NULL )
{
free ре; ре == NULL;
I
/*
Пример освобождения динамической памяти. занятой элементом
линейного списка. Среда языка С++
*/
±f( ре != NULL )
{
del&te ре; ре = NULL;
}
150
" матрицы \п" )/
return;
}
// Заполнение матрицы
£ог( 1=0; KRowSlze; 1 ++ )
{
£ог ( unsigned int j = 0; j<ColSlze; j++ )
{
retcode = fscanf( pStructlnp^
" %1" r &pMx[l ][ j ] ) ;
i£( retcode != 1 )
{
printf( "\n Ошибка 50. Ошибка чтения "
"элемента ма трицы \п" ) ;
return;
)
}
}
return;
;
int maln( void ) // Возвращает О при успехе
{
int "^^рМх; // Указатель на матрицу
unsigned г, // Число строк
с; // Число столбцов
// Ввод матрицы
ReadMatrlx ( "DynMem. Inp", г, с, рМх ) ;
151
/ / Здесь проверяем г, с , рМх
// . ..
// Освобождение динамической памяти, занятой матрицей
// Освобождение ДП, занятой строками матрицы
for( unsigned ±nt i=0; Кг; i++ )
{
delete [ ] pMx[ i ];
}
// Освобождение ДП, занятой массивом указателей на строки
// матрицы
delete [ ] рМх;
ret-arn 0;
}
152
первой. Прототип функции Initls^ выполняющей инициализацию
ЛС, ее определение и пример вызова приведены ниже. На данном
этапе рекомендуем из примера рассмотреть только часть програм
мы, предшествующую главной функции, и все, что относится к
функции Initls, Остальной материал будет рассмотрен далее.
Файл LS.CPP
Работа с динамической памятью. Однонаправленный линейный
список и операции с ним
// Прототипы функций
void Init_ls ( void. ) ;
void Dest^^ls ( void ) ;
void Add_end ( cha.r с ) ;
void Add__beg ( cbstr с ) ;
void Del_end ( void ) ;
void Del_beg ( void ) ;
void Create_end ( void ) ;
void Create__beg( void ) ;
void Print_ls ( void ) ;
void After_Add( char find,, сЪа.г add ) ;
void Before__Add ( char find, char add ) ;
void After_Del ( char find ) ;
void Before_Del ( char find ) ;
153
Add end( 'С ), // Добавление в конец списка
// элемента 'С'
After_Del ( '3'); // Удаление после '3'
Pr±nt_ls ( ) ;
Dest_ls ( ) ; // Разрушение списка
// Заполнение линейного списка символами из файла LS.DAT:
// последний прочитанный символ - в конце списка
Create_end( ) ;
Print Is( ) ;
Dest_ls ( ) ; // Разрушение списка
Add__end ( ' C ) ; // Добавление в конец списка
// элемента 'С'
Add_end ( 'D' ) ; // Добавление в конец списка
// элемента 'D'
Add beg( ^B^ ); // Добавление в начало элемента 'В
Add_beg ( 'A' ); // Добавление в начало элемента 'А
Print Is (r ) ;
Del_end( ); // Удаление последнего элемента
// списка
Del_beg( ); // Удаление первого элемента спи СК(
Print_ls ( ) ;
Dest_ls ( ) / // Разрушение списка
// Заполнение списка пятью двойками
for( Int i = 1; i <= 5; i-^+ )
Add__end( '2' ) ;
Print_ls ( ) ;
// Добавление '1 ' перед
Before_Add( ' 2 ' , Ч' ) ;
Print_ls ( ) ;
// Добавление '3 ' после
After_Add( ' 2 ' , '3' ) ;
Print_ls ( ) ;
// Добавление '1 ' перед
Before_Add( ' 1 ' , Ч' ) /
Print_ls ( ) ;
// Добавление '2 ' после
After_Add( ' 2 ' , '2' ) ;
Print_ls( ) ;
// Добавление '2 ' после
After_Add( ' 2 ' , '2' ) ;
Print_ls( ) /
// Добавление '3 ' после
After__Add( ' 3 ' , '3' ) /
Print_ls ( ) ;
After_Del ( '3'); // Удаление после
Print_ls ( ) ;
Before_Del( '1') // Удаление перед '1
Print_ls( ) ;
Dest_ls ( ) ; // Разрушение списка
return 0;
// Инициализация списка
154
void Init_ls ( void )
{
start = NULL; // Вначале список пуст
rebvLrn;
}
// Paзрушение списка
void Dest_ls ( void )
{
if( start == NULL )
{
printf( "\n Список пуст. Удалять нечего" ) ;
retuxm/
}
return/
}
155
cur~>next = temp;
}
return/
}
156
{
printf ( "\n Файл Is.dat не закрыт " ) ;
exi t ( 4 ) ;
}
return;
}
// Создаем список
while ( ( с = ( char )fgetc( f_in ) ) != EOF )
{
Add_beg ( с ) ;
}
// Закрываем файл
±f( ( fclose( f_in ) ) == EOF )
{
printf( "\n Файл Is.dat не закрыт " ) ;
exit(6);
}
rebum;
}
157
prev = NULL; end = start;
while( end->next /= NULL )
{
prev = end;
end = end->next; // Продвижение no списку
}
d&lete end; // 2: удаление последнего элемента
±f( prev != NULL )
{
// 3: бывший предпоследний элемент становится
// последним
prev~>next = NULL;
}
else
(
start = NULL; // 3: в списке был один элемент
}
return;
}
158
while ( prn != NULL ) // До конца списка
{
// Печать данных (символа) элемента
printf( " % с " , prn->ch ) ;
prn = prn~>next; // Продвижение по списку
}
return/
}
return/
/ у i^:h*iir-^-k-^irTlf*^Th-^-^9r-^ir*i^-k*-*c***Thilc-^*-ki^-k-k*-!h***T^
159
// Добавление элемента add перед элементом find
void Before_Add ( char find, cba.r add )
{
EL *temp, // Указатель на добавляемый элемент
*cur, // Указатель на текущий элемент
"^prev; // Указатель на элемент стоящий
// перед текущим)
±f( start =- NULL )
{
printf( "\n Список пуст. Нельзя найти нужный "
"элемент " ) ;
jretujrn/
;
// Поиск элементов, содержащих символ find, с добавлением
// перед ними элемента с символом add
сиг = start;
while ( cur /- NULL )
{
// Нужный элемент найден (он является текущим)
±f( cur->ch == find )
{
// 1: динамическое размещение элемента
temp = new EL;
if( temp == NULL )
{
printf( "\n Элемент списка не размещен" ) ;
exit ( 8 ) ;
}
// 2: занесение данных
temp->ch = add;
// 3: новый элемент указывает на элемент с
// символом find
temp->next = cur;
// 4: если элемент с символом find был первым,
// то start смещается влево (на новый элемент)
±£( сиг == start )
start = temp;
else
// 4: элемент, стоящий перед сиг указывает на
// новый
prev->next = temp;
}
prev = cur; // Продвижение текущего и
// предыдущего элементов
сиг = cur->next; // по списку
}
return;
}
160
EL *del, // Указатель на удаляемый элемент
*сиг; // Указатель на текущий элемент
±£( start == NULL )
{
printf( "\n Список пуст. Нельзя найти нужный "
"элемент " ) ;
jr&bVLJCZi;
}
161
printf( "\n Список пуст. Нельзя найти нужный "
"элемент " ) ;
}
±£( start->next == NULL )
{
printf( "\n в списке только один элемент. Нельзя "
"выполнить данную операцию" ) ;
return/
}
// Поиск элементов, содержащих символ find, с удалением
// элементов, предшествующих найденным
pprev = NULL/ cur = start->next/
while( cur != NULL )
(
±f( cur->ch == find )
{ // Нужный элемент найден (он является
// текущим)
// Найденный элемент является вторым в ЛС
±f( pprev == NULL )
{
// 1: будем удалять головной элемент
del = start/
// 2: первым элементом списка теперь будет
// бывший второй
start = start->next/
}
else
{
// 1: указатель на удаляемый элемент
del = pprev->next/
// 2: связь предпредыдущего и текущего
// элементов
pprev->next = del->next/
}
delete del/ // Удаление элемента
}
else
{ // Продвижение указателя pprev требуется, если на
// очередном шаге не было удаления элемента
±f( pprev == NULL )
pprev = start/
else
pprev = pprev->next /
}
cur = cur->next/ // Продвижение этого указателя
// требуется всегда
}
retujm/
}
162
1234567890
163
До выполнения операции
Общий случай Особый случай
Start = NULL;
• h> • W NULL
t Start
После выполнения операции
2: занесение данного «с» 2 занесение данного «с»
3: temp->next =
•—Н NULL NULL
Start;
t t
1: динамическое размещение 1. динамическое размещение
temp = new EL; temp = new EL;
t t
4: Start = temp; 4: Start = temp;
Рис. 46. Добавление элемента в начало линейного списка
164
До выполнения операции
Общий случай Особый случай
Start = NULL,
•—h> NULL
t Start
После выполнения операции
2: 2: занесение данного « о
4в: 3: temp->next =
NULL NULL
# •^ NULL;
t Start t t
1- 1-
динамическое размещение temp = new EL;
t t
46: cur
4B: cur->next = temp; 4a: Start = temp;
Рис. 47. Добавление элемента в конец списка
165
До выполнения операции
Общий случай Особые случаи:
а) в ЛС один элемент б)
Start = NULL;
• — — • ! •—^> • • ^ NULL NULL
I I
Start t Start
Вывод сообщения
и возврат из
NULL функции
• ^
t
1: del = Start; 1: del = Start;
t t
2: Start = del->next; 2: Start = del->next = NULL;
3: delete del; 3- delete del;
Рис. 48. Удаление элемента из начала списка
166
До выполнения операции
Общий случай Особые случаи:
а) в ЛС один элемент б)
Start = NULL;
•—и • — — • NULL 1 I NULL
t Start t Start
Вывод сообщения
L__ и возврат из
NULL функции
t t t t
start 1: prev end 1: prev = NULL;
end
2: delete end; 2: delete end;
3 prev->next = NULL; 3: Start = NULL;
Рис. 4 9 . Р а з р у ш е н и е л и н е й н о г о с п и с к а
167
До выполнения операции
Общий случай Особые случаи
find
Start = NULL,
w
NULL
,, .^
t Start
После выполнения операции
2:
Вывод
find add
сообщения и
4: 3: возврат из
W • w NULL
^ функции
t t t
Start cur 1: temp = new EL;
2: temp->ch = add;
3: temp->next = cur->next,
4. cur->next = temp;
t 5: cur = temp;
Рис. 50. Добавление элемента после каждого элемента ЯС,
содержащего заданное значение
168
До выполнения операции
Общий случай Особые случаи
t Start
После выполнения операции
2:
a) Вывод
add find сообщения и
4: 3: возврат из
• \-> •• W
' W NULL функции
—•
б) См.
t prev t cur
реализацию
шага 4
start
1: temp = new EL;
2: temp->ch = add;
3: temp->next = cur;
T 4: Start = temp при cur = Start
или prev->next = temp в остальных
случаях
169
8.14. Удаление элемента перед каждым элементом ЛС,
содержащим заданное значение
До выполнения операции
Общий случай Особые случаи
170
8.15. Зачем нужен линейный список
Для хранения и обработки информации наряду с ЛС можно
использовать и массивы. Например, вместо ЛС, приведенного на
рис. 45, можно использовать символьный массив из пяти элементов,
причем это сэкономило бы оперативную память. Значит ли это, что
ЛС не следует использовать? Конечно же, нет!
До выполнения операции
Общий случай Особые случаи
t Start
После выполнения операции
а) Вывод
find сообщения и
2: возврат из
NULL функции
w w
б) Вывод
t t t t сообщения и
возврат из
start pprev cur
функции
1 • del = pprev->next или
del = Start, если текущий
элемент в ЛС второй
2: pprev->next = del->next или
Start = Start->next, если текущий
элемент в ЛС второй
3: delete del,
171
на наихудший случай).
173
руживает это символическое имя в программе, то он заменяет имя
соответствующей строкой.
Для подстановки имен предусмотрены две директивы препро
цессора:
• создать макроопределение (Ude/ine);
• удалить макроопределение, т.е. сделать его неопределенным
{Uundef).
Пример использования директивы Udefine приведен на рис. 54.
174
тельна. Однако использование в именах макроопределения только
прописных букв позволяет легко отличить макроопределения от
других элементов программы.
Обратите внимание на то, что директивы включения препро
цессора не требуют завершающей точки с запятой. При ее наличии
точка с запятой войдет в текст макроопределения, замещающий имя
макроопределения. Это может стать причиной ошибки.
Следующей, достаточно распространенной, ошибкой является
включение пробелов в имя макроопределения, так как идентифика
тор не может содержать пробелов. Следует помнить, что для пре
процессора имя макроопределения заканчивается на первом же про
беле или табуляторе. Все символы, следующие за ним, рассматри
ваются как замещающая строка.
175
// Таблица площадей кругов
±nt main ( void. )
геЬмхпл 0;
2.Of/AREA(radius) заменяется на
2.0f/3.14*radius*radius, ошибка
AREA(start-end) заменяется на
3.14*start-end*start-end, также ошибка
176
9.3. Включение файлов
или
^include <путь_к_файлу>
177
Хотя многие из перечисленных средств нами еще не рассматрива
лись, было целесообразно привести здесь указанные сведения.
178
void, get line ( void. )
(
return;
#lfndef STDIO_H
^define STDIO_H
// Текст включаемого файла
#en'dlf
179
• i^error (обычно включается между директивами #(/'- if^endif для
проверки какого-либо условия на этапе компиляции; при выполне-
ниии такого условия компилятор выводит сообщение, указанное в
terror и останавливается);
• i^pragma (позволяет настраивать компилятор с учетом специ
фических особенностей конкретной машины или операционной
системы - указанные особенности индивидуальны для каждого
компилятора);
• import (имеет отношение к включению библиотек типов в
cow-технологии).
Их обсуждение выходит за рамки данного пособия.
i n t main ( void. )
begin
rGturn 0;
end
180
Большинство компиляторов языков Си/С++ поставляется вме
сте с набором заголовочных файлов. Одним из примеров такого ро
да является файл stdio,h. При использовании стандартных заголо
вочных файлов следует посмотреть их содержимое, чтобы случайно
не переопределить стандартное имя. Стандартные заголовочные
файлы разработаны квалифицированными программистами и по
этой причине их также целесообразно посмотреть.
10. РЕДКО ИСПОЛЬЗУЕМЫЕ СРЕДСТВА
ЯЗЫКОВ СИ/С++
182
• повышение удобства чтения программы;
• повышение мобильности программы.
Если typedef находится внутри функции, то его область дейст
вия локальна и ограничена этой функцией. Если же объявление име
ни типа расположено вне функции, то его область действия гло
бальна. В последнем случае typedef часто помещают во включаемые
файлы.
Определение объекта
о
Константа
перечислимого типа
{ >* перечисли ^ }
мого типа
Идентификатор
—•fenum V w перечислимого
типа
Идентификатор объекта
перечислимого типа
Константа <:>
перечислимого типа
Идентификатор
Константное выражение с
целочисленным значением
Рис. 55. Определение объектов перечислимого типа
183
enum languages{ с , разе, ada, modula2, forth };
languages master;
сазе с:
break;
сазе forth:
break;
cie£aul t:
184
Таким образом, константе ada соответствует значение 5, а константе
modula2 - значение 6. Разным константам перечислимого типа мо
жет соответствовать одно и то же значение {pasc^ forth).
Рассмотрим следующий пример:
#include <stdlo.h>
±nt mam ( vaid )
{
enxna t{ c==-l^ pasc=4^ ada, modula2, forth=4 };
t m, ml;
m = ada;
// Следующее присваивание не вполне корректно -
// компилятор формирует предупреждение, но программа
// выполняется
ml = 5 /
printf ( "\п т = %d, ml = %d", т, ml ) ;
// Данные присваивания также не вполне корректны -
// компилятор формирует предупреждение, но программа
// выполняется
т = О; ml = 6;
printf( " \ л т = %d, ml = %d, ada = %d", m, ml, ada ) ;
return 0;
185
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY, /* light colors */
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
enum graphics_errors
{ graphresult error return codes */
grOk = 0,
grNoIni t Graph = -i.
grNotDetected = -2,
grFileNotFound -^ -3,
grInvalidDriver = -4,
grNo Loa dMem = -5,
grNoScanMem = -6,
grNoFlооdMem = -7,
gr Font Not Found = -8,
grNoFon tMem = -9,
grInvalidMode = -10,
generic error */
grError = -11.
grIOerror = -12,
grInvalidFont = -13,
grinvalidFontNum = -14,
grInvalidVersion = -18
10.3. Объединения
Подобно структуре, объединение представляет собой агрегати-
рованный тип данных. Синтаксис объявления объединения иденти
чен синтаксису объявления структуры, только вместо служебного
слова struct используется служебное слово union. Различие между
структурой и объединением состоит в том, что каждому элементу
объединения выделяется одна и та эюе область памяти^ а не раз
личные области, как в структуре.
Синтаксис объединения поясняется следующей записью:
vLn±oTi [<идентификатор типа объединения>]
{
<тмп> <идентифмкатор>;
186
} [<список объектов-объединений>]/
Примеры:
union INT__OR_LONG
{
int 1;
long- 1;
} а_питЬег, b_number;
187
к каждому из этих байтов можно осуществить доступ по от
дельности, используя массив символов value.cvalue:
value, cvalue[ 3 ] // Доступ к 4 байту
ЮРА : 01С2
189
Значение сегментного регистра сдвигается влево на четыре
разряда (на одну шестнадцатиричную цифру) и к полученному зна
чению добавляется смещение. Как следует из рис. 56, начальный ад
рес сегмента всегда является двадцатибитовым числом, а так как
сегментный регистр имеет только шестнадцать бит, то недостающие
младшие четыре бита всегда подразумеваются равными нулю. Это
означает, что сегменты всегда начинаются на границе шестнадцати
байт или параграфа (отрезок памяти из смежных шестнадцати байт
называется параграфом).
Сегменты памяти могут быть смежными, разделенными, пере
крываться полностью или частично. Так как сегменты могут пере
крываться, то одна и та же ячейка памяти может быть адресована
более чем одним адресом. Например,
190
Аналогично формируется адрес команды в сегменте команд по
умолчанию (вместо регистра DS используется регистр CS).
Доступ к данным или командам из сегментов по умолчанию в
языках Си/С++ осуществляется через указатели near:
тип near *near_pointer/
Например,
int near *^_Р^'
Так как для доступа к данным или командам через адрес near
требуется только шестнадцатибитовая арифметика, то ссылки near
наиболее эффективны.
Например,
±nt far '^f_p;
191
указателей far также не гарантируется правильность выполнения
этих операций. Например, вычисление выражений
ptrl > ptr2 ptrl > ptr3 ptr2 > ptr3
192
11.2.Стандартные модели памяти
для шестнадцатибитной среды DOS
193
Компактная модель памяти. Используется в программах с
большим объемом данных и стека программы (более 64 Кбайт до 1
Мбайта) и небольшим объемом кода (не более 64 Кбайт). Компакт
ная модель памяти обеспечивает один сегмент для кода программы
и несколько сегментов для данных и стека программы. Поэтому в
программах с компактной моделью памяти для адресации кода ис
пользуются адреса near, а для адресации данных - адреса far.
194
^include <stdio.h>
// Массив huge из 70000 байтов
char hugre h_arr[ 10000 ] ;
Напечатается:
Размер массива h_arr: 44 64
195
return О;
/'^
Файл Р36. СРР (расширение .СРР принято для файлов с текста-
ми программ на C+-h) . Ра змещение определении данных внутри
блока
V
^include <stdio.h>
int mam ( void ) // Возвращает 0 при успехе
{
// В языке C-h-h "модно" таким образом определять и
// присваивать начальное значение управляющей
// переменной цикла for
fori xnt counterl = 0; counterl < 2; counterl++ )
// Переменная counterl "видна"^ начиная с этой строки и
// до конца та±п, а не только внутри блока for. Ей
// присваивается значение О перед входом в цикл
{
// Автоматической переменной 1 присваивается значение
// О при каждом проходе тела цикла,
±пЬ i =- О;
// а внутренняя статическая переменная j
197
// инициализируется нулем
static ±nt
J = О/
for( Int counter2 = 0; counter2 < 5; counter2++ )
pr±ntf( "\n ± = %d j = %d", i-h+r J++ ) ;
}
// counter2 "существует" до предыдущей фигурной скобки
char quit_message[ J = "\n До свидания! \n";
printf ( "%s", quit_message ) ;
ret-am 0;
}
198
/ / Рисуется окружность с центром в точке (200, 100) и
// радиусом 100
DrawCircle ( 200 );
// Рисуется окружность с центром в точке (200, 300) и
// радиусом 100
DrawCircle( 200, 300 );
// Рисуется окружность с центром в точке (200, 300) и
// радиусом 4 00
DrawCircle( 200, 300, 400 );
// Ошибка: аргументы можно опускать только справа
DrawCircle ( , , 400 );
int i = 2;
zr&tuxm 0;
}
199
12.3. Модификаторы const и volatile
Файл Р37,СРР
Модификатор const делает данное недоступным в других фай
лах программного проекта.
Состав проекта: Р37.СРР
CONST,СРР
*/
^include <stdio,h>
200
пилятора это означает, что, вычисляя значение выражения, в кото
рое вход;ит такое данное, он должен брать его значение только из
памяти (а не использовать копию, находящуюся в регистре, что до
пустимо в других случаях).
12.4. Ссылки
int X ^ 1;
int &xr = X / / Ссылка хг становится псевдонимом
// к
xr = 2; // Все равно, что к = 2;
ХГ+ + ; // Все равно, что х++;
Однако,
int X = 1;
// Так как типы х и хг не совпадают, то компилятор создает
// переменную типа char, для которой хг будет псевдонимом,
// и присваивает ей (char)х
char &ХГ = х;
хг = 2; // Значение х не изменяется
201
12.5. Подставляемые функции
int b = InlineFunctionCube( a ) ;
int с = InlineFunctionCube( a++ ) /
202
12.7. Перегрузка функций
±nt j =5/
print_lnt ( J ) ;
print_double( 3,14159 ) ;
print_string( "Hello" ) ;
203
±nt j = 5;
print( J );
print( 3.14159 );
print( "Hello" );
iretuxTi 0;
}
void f ( int = о ) ;
void f( void ) ;
// Отдельная функция
204
exteim "С" ±nt fund ( ±nt ) ;
extern "C" // Несколько функций
{
void. func2 ( ±nt ) ;
±nt funcS ( void. ) ;
double fun с 4( double ) ;
}
с = Ь; b = а; а = с/
return/
}
int mam ( void ) // Возвращает О при успехе
{
Int i = О, J = 1;
double X = 0.0, у = 1.0/
char *sl = "Строка!", *s2 = "Строка2" /
print f ( "\n Перед обменом: \n i = %d j = %d \n x=%lg "
"y==%lg \n sl=%s s2=%s", 1, J, X, y, si, s2 ) /
swap ( i , J ) / swap ( x, у ) / swap ( si, s2 ) /
printf ( "\n После обмена: \n i = %d j = %d \n x=%lg "
205
"у=%1д \п sl = %s s2^%s", i, j , x, y, si, s2 ) ;
rebvim 0;
}
^include <stdlo.h>
^include <string.h>
// Максимальная длина строки +1
const ±nt MAX_STR_LEN = 80;
206
// Длина строки результата равна сумме длин складываемых
// строк. Позаботимся также о том, чтобы не выйти за
// границу массива-суммы
±f( ( TmpStr. str_len = si . str_len + s2. str__len ) >=
MAX_STR_LEN )
{
TmpStr, s[ 0 ] = '\xO'/ TmpStr. Str__len = 0;
re turn TmpStr;
}
rebvLrii. 0;
13. ТЕХНОЛОГИЯ СОЗДАНИЯ ПРОГРАММ [5]
208
конченных фрагментов, а также обеспечить комментарии к про
грамме.
Каждый функционально законченный фрагмент алгоритма в
соответствии с технологией модульного программирования следует
оформить в виде функции. Каждая функция должна решать только
одну задачу (не надо объединять два коротких независимых фраг
мента в одну функцию). Предельные параметры функции (количест
во строк исходного текста и число параметров) определяются рас
смотренным ранее правилом "семь плюс-минус два".
Если некоторые действия встречаются в программе хотя бы
дважды, их также нужно оформить в виде функции. Однотипные
действия оформляются в виде перегруженных функций или функций
с параметрами. Короткие, простые функции следует оформлять как
подставляемые функции.
Необходимо тщательно выбирать имена объектов (пере
менных, функций и т.п.). Рационально выбранные имена могут сде
лать программу в некоторой степени самодокументированной. Не
удачные имена, наоборот, служат источником проблем. Не увлекай
тесь сокращениями - они ухудшают читаемость текста. Общая тен
денция состоит в том, что чем больше область видимости объекта,
тем более длинным именем его надо снабжать. Перед таким именем
часто ставится префикс типа (одна или несколько букв, по которым
можно определить тип объекта). Для управляющих переменных ко
ротких циклов, напротив, лучше использовать однобуквенными
именами типа /, у, или к. Имена макросов предпочтительнее записы
вать прописными буквами, чтобы отличать их от других объектов
программ. Не рекомендуется использовать имена, начинающиеся с
одного или двух символов подчеркивания, имена типов, оканчи
вающиеся на "_/" и т.п.
Переменные желательно инициализировать при их опреде
лении^ а определять как можно ближе к месту их непосредственного
использования. Но нет правил без исключений. Поэтому, с другой
стороны, все определения локальных переменных блока лучше рас
полагать в начале блока, чтобы их легко можно было найти.
Локальные переменные предпочтительнее глобальных. Если
глабальная переменная все же необходима, то лучше определить ее
статической. Это ограничит область действия такой переменной од
ним исходным файлом.
Всю необходимую функции информацию нужно стремиться
передавать через список параметров, а не через глобальные пере
менные, изменение которых трудно отследить.
Входные параметры функции, которые не дол:и€ны в ней
изменяться, следует передавать по ссылке с модификатором
const, а не по значению. Кроме улучшения читаемости программы и
209
уменьшения случайных ошибок, этот способ позволяет экономить
память при передаче сложных объектов. Исключение составляют
параметры, размер которых меньше размера указателя - их лучше
передавать по значению.
Нельзя возвращать из функции ссылку на локальную пере
менную, потому что она автоматически уничтожается при выходе из
функции, которая является ее областью действия. Не рекомендуется
также возвращать ссылку на объект, созданный внутри функции с
помощью функции malloc{ ) или операции new, так как это приводит
к трудно контролируемым утечкам динамической памяти.
Следует избегать использования в программе чисел в явном
виде. Константы должны иметь осмысленные имена, заданные через
const или епит (последнее предпочтительнее, так как память под
перечисление не выделяется). Символическое имя делает программу
более понятной. Кроме того, при необходимости изменить значение
константы это можно сделать всего лишь в одном месте программы.
Для записи каждого фрагмента программы необходимо ис
пользовать наиболее подходящие языковые средства. Любой цикл
можно, в принципе, реализовать с помощью операторов if и goto, но
это было бы нелепо, поскольку с помощью операторов цикла те же
действия легче читаются, а компилятор генерирует более эффектив
ный код. Ветвление на три или более направлений предпочтитель
нее программировать с использованием оператора switch, а не с по
мощью гнезда операторов if.
Следует избегать лишних проверок условий. Например, вме
сто операторов
±f( strstr( a,b ) > О ) { ... }
else ±f( strstr( a,b ) < 0 ) { . . . ;
else ( ... }
лучше записать
±nt ls_equal = strstr ( a,b ) ;
±f( is_equal > 0 ) { ... }
else ±f( is_equal < 0 ) { ... }
else { ... }
210
Если одна из ветвей условного оператора короче, чем другая,
то более короткую ветвь условного оператора лучше поместить
сверху, иначе вся управляющая структура может не поместиться на
экране, что затруднит отладку.
В некоторых случаях тернарная операция лучше условного
оператора:
if( Z ) 2.-J; else i=k; // i = z ? j : k;
211
требность в каких-то ее новых свойствах появляется сразу же после
ввода программы в эксплуатацию и сопровождение программы за
нимает гораздо больше времени, чем ее разработка. По этой причи
не основная часть документации долэюна находиться в тексте про
граммы. Хорошие комментарии написать почти так же сложно, как
и хорошую программу.
а Комментарии долэюны представлять собой правильные
предложения без сокращений и со знаками препинания и не должны
подтверждать очевидное. В данной книге иногда есть отступы от
этого в учебных целях.
а Если комментарий к фрагменту программы занимает не
сколько строк, его лучиге разместить до фрагмента, чем справа от
него, и выровнять по вертикали. Абзацный отступ комментария
долэюен соответствовать отступу комментируемого блока.
• Для разделения функций и других логически законченных
фрагментов пользуйтесь пустыми строками и комментариями вида:
//* ii^ic-kic*i(i<irk*i(iciri(:*iricici(*i(**i^ic-kic*icif9c*i(-k^ic**ic-kiricici<*-k-^iri(i(icici^*ic*if*
212
еле некоторого перерыва написанные фрагменты стираются и все
повторяется заново.
В процессе работы неоднократно изменяются структуры дан
ных, разбиение на модули (функции) делается только тогда, когда
просматривать текст программы становится неудобно и утомитель
но. При этом комментарии к программе не пишутся, а ее текст не
форматируется. Из-за многочисленных неудач периодически выска
зываются сомнения в правильности работы компилятора, компьюте
ра и операционной системы.
Когда программа впервые доходит до стадии выполнения (а
это случается не скоро), в нее вводятся произвольные исходные
данные, после чего экран компьютера и файл результатов становят
ся объектами пристального удивленного изучения. "Работает" такая
программа обычно только на одном наборе исходных данных, а вне
сение в них даже небольших изменений приводит автора к потере
веры в себя и портит его настроение.
Задача настоящего программиста состоит в том, чтобы нау
читься подходить к программированию профессионально. Профес
сионал отличается тем, что может достаточно точно оценить, сколь
ко времени займет разработка программы, которая будет работать в
точном соответствии с поставленной задачей. Для достижения по
добных результатов требуется склонность к программированию,
опыт, а также знание основных принципов, выработанных програм
мистами в течение более чем полувека развития этой дисциплины.
Даже к написанию самых простых программ нужно подходить по
следовательно, проходя в соответствии со структурным подходом
ряд последовательных этапов.
Структурный подход к программированию охватывает все
стадии разработки проекта — спецификацию, проектирование, соб
ственно программирование (кодирование) и тестирование. При этом
стремятся к уменьшению числа возможных ошибок, их более ран
нему обнаружению и упрощению процесса исправления ошибок. В
рамках структурного подхода используются нисходящая разработка,
структурное и модульное программирование и нисходящее тестиро
вание.
Рассмотрим этапы создания программ, рассчитанные на дос
таточно большие программные проекты, разрабатываемые коллек
тивом программистов [5]. Для неболыиих программ каждый из та
ких этапов упрощается, но содержание и последовательность эта
пов не изменяются,
213
Изначально задача ставится в терминах некоторой предметной об
ласти и необходимо перевести ее в термины, более близкие к про
граммированию. Поскольку программист редко досконально разби
рается в предметной области, а заказчик - в программировании, то
постановка задачи может стать весьма непростым итерационным
процессом. Отметим, что здесь весьма полезным является использо
вание объектно-ориентированного подхода, средства реализации
которого в языке C++ будут рассмотрены во второй части книги.
Постановка задачи заканчивается созданием технического за
дания, а затем и внешней спецификации программы, которая вклю
чает в себя.
а Описание исходных данных и результатов (виды,
представление, точность, ограничения и т.п.).
• Описание задачи, реализуемой программой,
а Способ обращения к программе.
• Описание возможных особых и аварийных ситуаций и оши
бок пользователя.
На этом этапе программа рассматривается как "черный ящик",
для которого определена выполняемая им функция, входные и
выходные данные.
214
низком уровне абстракции и т.д. Очень важной на этом этапе явля
ется спецификация интерфейсов, т.е. способов взаимодействия
подзадач.
Для каждой подзадачи создается внешняя спецификация, ана
логичная указанной для этапа 1. Здесь же решаются вопросы раз
биения программы на модули. Декомпозиция выполняется таким
образом, чтобы минимизировать взаимодействие модулей. В резуль
тате может оказаться так, что одна задача реализуется с помощью
нескольких модулей и, наоборот, в одном модуле может решаться
несколько задач. На более низкий уровень проектирования перехо
дят только после окончания проектирования верхнего уровня. Алго
ритм записывают в обобщенной форме — например, текстуальной, в
виде обобщенных блок-схем или другими способами.
На этапе проектирования следует учитывать возможность бу
дущих модификаций программы и стремиться проектировать про
грамму таким образом, чтобы вносить изменения было возможно
проще. Процесс проектирования является итерационным, поскольку
в программе реального размера трудно продумать все детали с пер
вого раза.
215
граммирование может привести к огромным затратам на поиск оши
бок на этапе отладки.
Этапы проектирования и программирования совмещены во
времени: в идеале сначала проектируется и кодируется верхний уро
вень, затем следующий за ним и т.д. Такая стратегия хороша тем,
что в процессе кодирования может возникнуть необходимость вре-
сти изменения, отражающиеся на модулях нижнего уровня.
216
сов, приступают к кодированию следующего уровня программы.
Естественно, что полное тестирование программы, пока она
представлена в виде скелета, невозможно, однако добавление каж
дого следующего уровня позволяет постепенно расширять область
тестирования.
Этап комплексной отладки на уровне системы при нисходя
щем проектировании занимает меньше времени, чем при восходя
щем, поскольку вероятность появления серьезных ошибок, затраги
вающих большую часть системы, гораздо ниже. Кроме того, для ка
ждого следующего, подключаемого к системе модуля уже создано
его окружение и выходные данные отлаженных модулей можно ис
пользовать как входные для тестирования других. Но это, конечно,
не значит, что модуль можно подключать к системе совсем "сырым"
- бывает удобным провести часть тестирования автономно, по
скольку сгенерировать на входе системы все варианты, необходи
мые для тестирования отдельного модуля, трудно.
В приложении П.4 рассмотрена более подробно методика от
ладки программы применительно к возможностям двух популярных
разновидностей интегрированных сред проектирования программ.
ЧАСТЬ 2. ПРИКЛАДНОЕ ПРОГРАММИРОВАНИЕ
218
оперативной памяти должен быть известен до начала выполнения
программы и задан в виде константы. Заметим, что это возможно не
всегда и тогда используют динамическое размещение данных в опе
ративной памяти. Динамическое размещение данных в памяти про
исходит во время выполнения программы с помощью операции new
(только для языка С-+"Ь) или функции malloc (языки Си/С++). При
этом необходимый объем требуемой памяти должен быть известен
лишь к моменту динамического распределения памяти, а не до нача
ла выполнения программы.
Как уже было сказано, если до начала работы с данными не
возможно определить, сколько памяти потребуется для их хранения,,
то память выделяется по мере необходимости отдельными блоками,
связанными друг с другом с помощью указателей. Такой способ ор
ганизации получил название динамических структур данных, по
скольку их размер изменяется во время выполнения программы. В
качестве таких структур в программах часто используются линейные
списки (см. разд. 8), бинарные деревья и очереди, частным случаем
которых являются стеки. Они отличаются способами связи отдель
ных элементов друг с другом и допустимыми операциями. Отметим,
что динамическая структура данных может занимать несмежные
блоки оперативной памяти.
Динамические структуры данных часто применяют и для бо
лее эффективной работы с данными, размер которых известен. К
такого рода случаям можно отнести решение задач сортировки и
поиска элементов. При сортировке упорядочивание динамических
структур не требует перестановки элементов, а сводится к измене
нию указателей на эти элементы. Это особенно эффективно, если
сортируемые элементы большого размера. При решении задачи по
иска элемента в тех случаях, когда важна скорость, данные лучше
всего представлять в виде бинарного дерева.
Элемент любой динамической структуры данных представляет
собой структуру {struct), содержащую, по крайней мере, два поля -
для хранения данных и для указателя (см. разд. 8). В общем случае
полей данных и указателей может быть несколько. Поля данных мо
гут быть любого типа: стандартного (основного), составного или
типа указатель.
219
дыи элемент вторую ссылку — на предыдущий элемент, то получится
двунаправленный список (двусвязный). Если же последний элемент
связать указателем с первым, получится кольцевой список.
Каждый элемент списка содержит ключ, идентифицирующий
этот элемент. Ключ часто бывает целым числом или строкой и явля
ется частью поля данных. В качестве ключа в процессе работы со
списком могут выступать различные части поля данных (например,
фамилия, возраст, стаж работы и др.). Ключи разных элементов спи
ска, в частном случае, могут совпадать.
Над списками можно выполнять различные операции. Список
операций над однонаправленным линейным списком и их реализа
ция были рассмотрены в разд. 8.
220
Корень
J1=2*i
_Z^X J
2, 3, 4, 5 - вершины
10 6, 7, 8, 9, 10-листья
8
221
14.4. Реализация динамических структур
с помощью массивов
222
мить как поля структуры. В качестве упражнения можно предлага
ется самостоятельно и для этого случая составить иллюстрирующий
пример.
0 8 9 1
2 6 4 5
224
ir
Рис. 59. Последовательный доступ
225
рядоченный тип).
Метод сортировки называется устойчивым, если относитель
ный порядок элементов с одинаковыми ключами не меняется при
сортировке. Устойчивость сортировки часто бывает желательной,
если элементы упорядочены по каким-то вторичным ключам, т.е. по
свойствам, не отраженным в первичном ключе.
226
• сортировка выбором;
• сортировка вставками;
• сортировка обменом.
Рассматриваемые программы будут работать с указателем агг
на массив, компоненты которого нужно отсортировать "на месте", и
структурным типом ELEMENT^ определенным выше.
Указатель агг на массив можно определить так:
±пЬ / / Размер массива
// Указатель на сортируемый массив
ELEMENT *агг = new ELEMENT[ s } ;
max s-1
227
1 1-1 S-1
—\ \ 1 г
i= 1,2, ..., s-1
S-2 S-1
и т.д.
Рис. 62. Сортировка простым обменом
228
Начальные значения 44 55 12 42 94 18 06 67
ключей элементов
массива 44 55 12 42 67 18 06 1 94
м
44 55
^
12 42 06
«"1 67 94
44 18 12 42 06 1 55 67 94
м—
Об 18 12 42 1 44 55 67 94
Ф
06 18
."1 42 44 55 67 94
06 18 42 44 55 67 94
06 12 18 42 44 55 67 94
Рис. 63. Сортировка массива простым выбором
/*
Файл TestSortArr.срр. Тестирование сортировки динамически
размещенных массивов с использованием различных методов.
Определение методов сортировки приведено в файле
SortArr.срр.
Определение Функций размещения массива в динамической па
мяти и освобождения занятой динамической памяти находится в
файле А1 ocFreeDM. срр,
Определение функций ввода и печати значений элементов мас
сива дано в файле SortlnOut.срр.
Заголовочный файл программного проекта дан в файле Sor
tArr. h.
Давыдов В.Г. Консольное приложение. Visual C++ 6
Ч
229
// задается; ArgV[ 1 ] - файл ввода; ArgVf 2 ] - файл
// вывода)
сЪаг *ArgV[ ] )
{
// Проверка числа аргументов командной строки
±f( ArдС /= 3 ;
{
printf(
"\п Ошибка 5. В командной строке должно быть три аргумента: "
" \ л Имя_проекта. ехе имя_файла_ввода имя_файла__вывода \п" ) ;
ех11 ( 5 ) ;
}
230
"выбором. Массив до сортировки \п ", ArgVf 2 ],
"а " ) ;
TreeSort( arr ) ;
WriteArr( arr, "\n Массив после "
"сортировки \п ", ArgV[ 2 ], "а" ) ;
xetujrn 0;
;
/*
Файл SortArr.h. Подключение стандартных заголовочных фай
лов, объявление объектов с описателями класса хранения "внеш
ний", типов элементов сортируемого массива, стека и прототи
пов функций. Используется как заголовочный файл в программном
проекте для сортировки массивов.
Давыдов В.Г. Консольное приложение. Visual C++ 6
V
// Предотвращение возможности многократного подключения
#ifndef SORTARR__H
^define SORTARR Н
231
^include <stdio.h> // Для ввода-вывода
^include <stdlib.h> // Для exit ( )
232
iendlf
_
Файл AlocFreeDM. cpp. Размещение одномерного массива в ди
намической памяти и освобождение динамической памяти, занятой
массивом.
Используется в программном проекте для сортировки масси
вов.
Давыдов В.Г. Консольное приложение. Visual C-h-h 6
V
// Включаемый файл программного проекта
Unciude "SortArr.h"
2retuz*n/
}
233
void AllocArrDMl (
ELEMENT *&arrl, // Указатель на начало массива в
// динамической памяти (передаем
// по ссылке - это ответ)
±nt S ) // Число элементов массива
{
// Контроль корректности размера массива
±£( S < 3 )
{
print f ( "\п Предупреждение 10. Массив должен "
"содержать более трех элементов \п (задан "
"размер, равный %d) . Принимается размер "
"массива г равный 3,\п Выполнение программы
"продолжается " ) ;
S = 3;
}
return;
}
retvLrn;
234
Используется в программном проекте для сортировки масси
вов.
Давыдов В.Г. Консольное приложение^ Visual C++ 6
V
// Включаемый файл программного проекта
§ include "SortArr.h"
235
return/
}
return/
236
// Печать значений элементов массива в файл результатов
void. Wri teArr (
ELEMENT arr[ ]^ // Сортируемый массив
char '^'pMSG^ // Указатель на строку-сообщение о
// печатаемом массиве
char *pOutFile^// Указатель на имя.расширение
// файла результатов
char *Mode ) // Указатель на режим открытия файла
{
FILE *pStrOut; // Указатель на структуру со
// сведениями о файле результатов
int 2, // Индекс элемента массива
RetCodel; // Возвращаемое значение fclose( )
// Открытие файла вывода
pStrOut = fopen ( pOutFlle, Mode ) ;
±£( pStrOut = - NULL )
{
printf( "\n Ошибка 60. Файл %s для вывода не '
"открыт \л", pOutFile ) /
exit( 60 ) ;
}
return;
}
237
char *pMSG, // Указатель на строку-сообщение о
// печатаемом массиве
cha.2: *pOutFile, // Указатель на имя .расширение файла
// результатов
cbajT *Mode ) // Указатель на режим открытия файла
return/
}
Файл SortArr.cpp.
Функции сортировки динамически размещенного массива по не
убыванию:
* простая сортировка массива выбором;
* простая сортировка массива обменом;
* простая сортировка массива вставками;
* сортировка массива с помощью двоичного дерева;
* сортировка Шелла;
* не рекурсивная сортировка Хоора;
238
'*' рекурсивная сортировка Хоора.
Используется в программном проекте для сортировки масси
вов.
Давыдов В.Г. Консольное приложение, Visual C++ 6
V
// Включаемый файл программного проекта
^include "SortArr.h"
239
±f( arr[ к ] , key > max. key )
(
indmax = k; max = arrf к ];
}
}
return;
)
return;
}
240
sorted = О;
// Цикл проходов
while ( !sorted )
{
change = О;
fori к = 1; к < size; к++ )
{
±f( arr[ k-1 ].key > arr[ к ].key )
{
temp = arr[ к ]; arr[ к J = arr[ k-1 J;
a r r / " k-1 ] = temp;
change = 1;
}
}
sorted = !change;
}
retvum;
}
241
etlse
{ // Следующий внизу уровень есть
±£( jl == last )
{
J = Jl/
}
else
{
j = ( arrf J1-1 J.key >= arrf j2-l ] . key )
? jl : j2;
}
// Выяснение, кто заполняет "дыру"
±f( arr[ j-1 ],key <= copy.key )
{
found = 1;
}
else
{
arr[ i-1 ] = arr[ j-1 ]; i=j;
}
}
}
arrf i-1 ] = copy;
return;
}
// Сортировка
void TreeSort (
ELEMENT arrf ]) // Сортируемый массив
{
±nt temproot, // Индекс корня частичного поддерева
templast; // Последний элемент в
// неупорядоченном поддереве
ELEMENT tempcopy; // Для перестановки элементов
242
}
xretuxm/
}
d = size;
while ( d > 1 )
{
d = d/2;
243
retuxm/
}
return;
}
//
// Извлчение сегмента из стека
void Pop (
int &i, // Указатель на левую границу
// сегмента
int &r, // Указатель на правую границу
// сегмента
STACK s[ ], // Стек границ сегментов
int &sp ) // Указатель вершины стека
(
1 = s[ sp J.l; г = s[ sp ].r; sp--;
re trim;
}
//
// Быстрая сортировка массива - нерекурсивный вариант
void Quicksort(
ELEMENT arr[ ] ) // Сортируемый массив
{
int left, // Левая граница разделяемого
// сегмента
right; // Правая граница разделяемого
// сегмента
// i - индекс кандидата на обмен слева - направо (объект
// определен на внешнем уровне)
// j - индекс кандидата на обмен справа - налево (объект
// определен на внешнем уровне)
// median - медиана разделяемого сегмента (объект
244
// определен на внешнем уровне)
// сору - для перестановки кандидатов (объект определен
// на внешнем уровне)
STACK s[ М ]; // Стек границ сегментов
±Tib sp; // Указатель вершины стека
return;
}
245
(
Split ( Or size-1 );
return;
}
//
// Функция разделения сегментов
void Split (
±nt leftr // Левая граница сегмента
int right ) // Правая граница сегмента
{
xf( ( right-left ) <= О )
(
return;
}
else
(
// Подготовка
median = arr[ ( left + right )/2 ]; i = left;
j = ri gh t;
// Разделение
while( i <= j )
f
// Найти кандидата на обмен слева
while ( arr[ i ].key < median.key ) i + + ;
// Найти кандидата на обмен справа
while ( median.key < arrf j J.key ) j - - ;
return;
246
44 55 12 42 94 18 6 61
247
3. Оформление включаемого файла программного проекта.
248
44 1 55 12 42 94 18 06 67 i = 2
ключей элементов
44
^ 1
55 1 12 42 94 18 06 67 1=3
массива
1
12 44 55 1 42 94 18 06 67 i = 4
^ 1
12 42 44 55 1 94 18 06 67 1 =5
12 42 44 55 94 1 18 06 67 1 =6
12 18 42 44 55 94 1 06 67 i = 7
06 12 18 42 44 55 941 67 i = 8
Массив отсортирован 06 12 18 42 44 55 67 94
0 1 2 ... sJze-1
I г 1 1 ~ "Г
агг
«Барьер» Сортируемый массив
Рис. 65. Использование "барьера" при сортировке массива
ростыми включениями
249
при i-oM просеивании составляет самое большее /, а самое меньшее
- 1. Число М, пересылок (присваиваний) элементов при /-ом
просеивании равно
(С,-1) + 3 = С,+2
Это объясняется тем, что тело цикла while выполняется на
один раз меньше, чем число проверок условия повтора цикла. Три
других пересылки при /-ом просеивании есть:
сору = агг1[ 1 ]; arrl [ О ] = сору/ arrl [ j+1 ] = copy;
^MiN = 3 • (size - 2), MMAX = X (' "^ ^) = ^^^^^ "^ ^) * ^^^^^ " 2) / 2,
250
Начальные значения ключей 44 55 12 42 94 18 06 6
элементов массива (size = 8)
12 55 18 94
42 55 06 94
<— — •
Конец первого просмотра 44 12 42 55 18 06 67 I 94
Обратите внимание как «пузырек» 94 «всплыл» вправо!
< • м •
12 44 18 55
42 44 06 55
Конец второго просмотра 12 42 44 18 06 55 I 67 94
18 44
06 44
Конец третьего просмотра 12 42 18 06 44 I 55 67 94
-•
18 42
06 42
Конец четвертого просмотра 12 18 06 42 ( 44 55 67 94
06 18
Конец пятого просмотра 12 06 18 I 42 44 55 67 94
^4-
06 12
Конец шестого просмотра 06 12 | 18 42 44 55 67 94
И конец сортировки, так как больше перестановок нет!
Рис. 66. Пример сортировки массива простым обменом
251
сортировки массивов показывают, что наиболее быстрой является
сортировка вставками, а наиболее медленной - сортировка обме
ном.
3. Несмотря на плохое быстродействие, простые алгоритмы
сортировки следует применять при малых значениях size.
4. Наряду с простыми алгоритмами сортировки массивов су
ществуют сложные алгоритмы сортировки, обеспечивающие время
сортировки, пропорциональное не size^, а size-logjisize). При больших
значениях size они обеспечивают существенный выигрыш. К их рас
смотрению мы и переходим.
252
Всего ( 2*size-1 ) вершин
94
253
требуется дерево с size вершин. У каждой вершины может быть О, 1
или 2 дуги вниз (О -лист, 1 или 2 дуги вниз - промежуточная верши
на или корень).
Для рассмотренного выше примера мы получаем следующее
дерево из size вершин (рис. 69).
44
55 12
^ ^ о \..^^
42 94 18 06
/ А с:
67
254
Size/2=4
В корне соответствующего поддерева образуем «дыру» путем
копирования соответствующего корню элемента в сору. Ищем
подходящее место для вставки сору и помещаем в него сору.
«Дыра»
1) Г — ;
42 -• j 1 1 42 j 67
4 сору 4 ^^^^ сору 4
67 W 67 3) ^ 42
8 8
Рис, 70. Подготовка двоичного дерева для вершины 4
Size/2 - 1 = 3
1) Г 1
12 -• 1 1 12 18
/ 3 \ сору 3 сору
18 06 ^ 18 <;
3)
06 w 12 06
6 6
Рис. 7 1 . Подготовка двоичного дерева для вершины 3
Size/2 - 2 = 2
2 сору
1) Г :
55 1 55 1 94
67
X 2 \
94
^ сору ^_д
W
X 2)^\Ч 3)
67 94 ^
/4"^
67 55
255
3. функция просеивания
3.1. Идея функции (см. рис. 57)
±ль root^ // Корень дерева ими поддерева
last; // Номер последней вершины дерева
// или поддерева
Size/2 - 3 == 1
a) 6) в)
copy copy
1) Г--1 ( i
44 i 44 i 94 1 44 1
1 copy к 1 • ^ " ^ l " ^ " " ^
94 18 94 18 ^ 18
1 1 1 1 1 1 1 1 ^)ш\ L 1 1
2 3 2 3 - ^ 2 ^;^ 3
67 55
с)) в итоге nonv Ч И М
67
2
44 55
/ 4 fS
42
256
3.2. Функции просеивания и сортировки сложным выбором.
Прототипы функций просеивания (Sift), сортировки сложным
выбором с помощью двоичного дерева (TreeSort), их определения и
примеры вызовов содержатся в примере программы, приведенном
выше в подразд. 15.2. Обратите внимание на то, что функция про
сеивания используется только для внутренних целей и, таким обра
зом, не является интерфейсной (вызывается из функции сортировки
сложным выбором). Функция же сортировки сложным выбором, на
против, является интерфейсной. Это означает, что она вызывается
пользователем для сортировки массива.
Идея алгоритма,
1. Используется несколько проходов.
2. На первом проходе отдельно группируются и сортируются
вставками элементы, отстоящие друг от друга на (i = size/2 позиций.
3. На втором проходе аналогично группируются и сортируют
ся вставками элементы, отстоящие друг от друга иг. d = d/2 позиций.
4. Аналогично выполняются последующие проходы и сорти-
257
ровка заканчивается последним проходом при d - I (как при про
стой сортировке вставками).
Иллюстрирующий пример дан на рис. 74.
Следует подчеркнуть, что ускорение сортировки происходит
на первых этапах, когда сортировка вставками производится среди
элементов, отстоящих друг от друга на болыиие расстояния. По
этой причине на втором и последующих этапах перестановки эле
ментов почти отсутствуют.
18 55
06 12
В результате получаем 44 18 06 42 94 55 12 67
Второй этап при d = d/2 = 2 • •
06 44
06 12 44 94
В результате получаем 06 18 12 42 44 55 94 67
Третий этап при d = d/2 = 1 (последний): сортировка производится как
простая сортировка вставками
В результате получаем 06 12 18 42 44 55 67 94
Рис. 74. Иллюстрирующий пример для сортировки Шелла
258
^,=1, J, = 3 - ^ , _ , + 1 (A: = 2,3,...)
1,4, 13, 40, 121, ... (в обратном порядке)
Анализ показывает, что число сравнений при этом пропорцио
нально size^'^, а не size^'^ ^ как в нашем варианте. И то, и другое при
больших size хуже, чем size-Xogj^size),
п
median
median = arr[ (left+right )/2 ]
Рис. 75. Идея алгоритма сортировки Хоора
259
a)
i = left, while( arr[ i ].key < median.key ) i++;
left
Всегда ли закончится цикл? Да, всегда,
поскольку, как минимум, справа есть median.
После выхода из цикла получим
п
median
агг[ I ] key >= median.key
Q)
j = right; while( arr[ j ] key > median.key ) j—;
right
По тем же причинам, цикл будет
заканчиваться всегда и после его завершения
п
получим агг[ j ].кеу <= median key
median в)
Если i <= j , то divrl i ] и arr[ j ] меняем местами: copy = arr[ i ]; arr[ i ] = arr[ j ];
arr[ j ] = copy; i++; j - ;
г)
Перейти к a), если i <= j
261
в) Больший из полученных сегментов (левый) запоминаем в стеке для
последующего разбиения. Работа с правым (меньшим) сегментом
закончена, так как в нем один элемент. Извлекаем из стека и разбиваем
очередной сегмент
left right
arr[j] key 06 i=0; j=1,0; arr[0] меняем местами с arr[0]; i++, j -
12 i>j (разбиение на сегменты закончено). Оба
полученных сегмента единичной или нулевой
median А^'^чь! и работа с ними закончена
262
Пример 2
left nght
Исходные значения ключей элементов 3 2 - 7 5 4
массива агг[ i ].кеу (i = О, 1, .. , size-1), size = О
median=arr[(left+right)/2]
Пример 3
left right
Исходные значения ключей элементов 3 2 5 - 7 4
массива агг[ i ].кеу (i = О, 1, ..., size-1), size = 1
median=arr[(left+right)/2]
Пример 4
left right
Исходные значения ключей элементов 3 2 4 - 7 5
массива агг[ 1 ].кеу (1 = О, 1, ..., slze-1), size = 0 1
, median=arr[(left+right)/2]
Рис. 78. Частные случаи для сортировки Хоора
263
вызовов-функций. Это означает, что имеются вложенные друг в дру
га активные экземпляры рекурсивной функции.
2. Каждый рекурсивный вызов помещает в системный стек:
копии параметров рекурсивной функции, передаваемых по
значению;
адреса аргументов из вызова рекурсивной функции, соответст
вующих параметрам, передаваемым по ссылке;
ее автоматические переменные;
адрес возврата из функции;
возвращаемое значение, если оно имеется.
По этой причине в рекурсивной функции лучше иметь меньше па
раметров и автоматических переменных (помните, что поколений
активных экземпляров рекурсивной функции может быть много).
3. На рекурсивный вызов тратится больше времени, но зато
программа получается нагляднее и проще.
264
изводительности из сложных сортировок (сортировка Шелла).
При увеличении размеров массива, указанные в пп. 1 и 2 эф-
фекты проявляются еще в большей степени.
16,1. Терминология
266
{a,b) G R тогда и только тогда, когда {Ь,а) е R
а b а b
О Ю о о
а - предшественник вершины "Ь"
b - преемник вершины "а"
а) б)
Рис. 80. Граф:
а) ориентированный (направленный);
б) неориентированный
267
100 10.0
10.0
Рис. 81. Пример взвешенного неориентированного графа
а 20 b а 20 b
о -О о -ю
а О 20 а 0 20
ь| 20 I О I b 0 0
а b а
а) б)
Рис. 82. Способы задания графа
268
/ / Адрес первого элемента массива структур с информацией о
// ребрах графа
А *рАгс;
269
ных путей). Нас при поиске оптимального пути интересует на каж
дом этапе только один путь, а не все сразу, т.е. требуется последова
тельный перебор путей. Из информации на рис. 83 следует, что для
перебора путей хорошо подходит рекурсивный алгоритм (аналогия с
вычислением факториала).
р,
Вершина
Путь
Ребро •
270
start finish start finish
1 4 5 2 1 2 3 4 5
1 1 1 1 exist 1 1 1 1 1
0.0 10.0 20.0 30.0 SumDist 0.0 30.0 30.0 10.0 20 0
0 1 4 5 ref 0 5 4 1 4
J t i
t
(REFerence - ссылка): 0 - конец списка
a) 6)
Рис. 84. Представление кратчайшего пути между вершинами:
а) с помощью линейного списка;
б) на базе массива структур
•в;.-; •о
Рис. 85. Поиск минимального пути
271
start
О • О
intermediate finish
а) intermediate = finish
Вершина Конец
б) intermediate != finish
intermediate
•>\ Ребро Путь
Ill
Файл TestGr.cpp. Тестирование решения транспортной задачи
с размещением данных в динамической памяти.
Определение функций^ используемых при решении транспортной
задачи, приведено в файле Graph.срр.
Определение Функций размеш,ения данных в динамической памя
ти и освобождения занятой динамической памяти находится в
файле GrAlocFree.срр.
Включаемый файл программного проекта находится в файле
GrHead.h.
Давыдов В.Г. Консольное приложение, Visual C-f-f- 6
V
/ / Включаемый файл программного проекта для решения
// транспортной задачи
^include "GrHead.h"
±nt main ( // Возвраш,ает О при успехе
±пЬ АгдС, // Число аргументов в командной
// строке
cha.r *ArgV[ ] ) / / Массив указателей на аргументы
// командной строки
{
// Проверка числа аргументов командной строки
±f( ArgC /= 3 )
{
printf(
"\п Ошибка 5. В командной строке должно быть три аргумента:
"\п Имя__проекта. ехе имя_файла_ввода имя_файла_вывода \п" ) ,
exit ( 5 ) ;
}
•returri Or
}
273
_v
// Предотвращение возможности многократного подключения
iifndef GRHEAD_H
^define GRHEAD Н
// Прототипы функций
void GrAllocDM( void, ) ;
void GrFreeDM( void ) ;
void ForStep ( int, int, int ) ;
void PassWay ( int ) ;
void solution ( void ) ;
void OutRes ( char* *, char *pMode ) ;
void ReadGraph ( char *pFlleInp ) ;
274
void WriteGraph ( char ^pFileOut, char *pMode ) ;
^endif
275
// Размещение структур данных графа в динамической памяти
GrAllocDM( ) ;
// Заполнение массива ребер графа
Ret Code = 0;
£ою ( i = 0; 1 < Gr.NumArc; i + -h )
{
RetCode += fscanf( pStrlnp, " %d %d %g",
&Gr.pArc[ i ].first, &Gr.pArc[ 1 J.last,
&Gr.pArc[ 1 ].weight ) ;
±f( ( Gr.pArcl i ].first < 0 ) \\
( Gr.pArcl i ].first >= Gr.NumTop ) | |
( Gr.pArc[ i ].last < 0 ) \\
( Gr.pArcf 1 ].last >= Gr.NumTop ) )
{
printf( "\n Ошибка 75. Индексы вершин д.б. в "
"диапазоне О. . %d \ л " , Gr.NumTop~l ) ;
exi t ( 75 ) /
}
}
±£( RetCode < 3*Gr.NumArc )
{
printf( "\n Ошибка 80. Ошибка чтения элементов "
"массива ребер \п" ) ;
exit ( 80 ) ;
}
xretuxm/
276
// Печать данных о графе
void. WriteGraph (
char *pFileOut,// Указатель на файл результатов
char *pMode ) // Указатель на режим открытия файла
{
FILE *pStrOut/ // Указатель на структуру со
// сведениями о файле результатов
Int i, / / Индекс элемента массива ребер
RetCodel; // Возвращаемое значение fclose( )
return;
}
277
Давыдов В.Г. Консольное приложение^ Visual C++ 6
_V j
/ / Включаемый файл программного проекта для решения
// транспортной задачи (задачи коммивояжера)
#include "GrHead.h"
11^
// Размещение массива структур с информацией о наилучшем
// пути в динамической памяти
pMlnWay = new W[ Gr. NumTop ] ;
±f( pMinWay == NULL )
{
printf(
"\n Ошибка 40. Массив структур с информацией о наилучшем"
" пути в \п"
"\п динамической памяти не размещен " ) ;
exit ( 4 0 ) ;
}
return;
}
return;
}
279
графе;
* печать информации о наилучшем пути от start до finish.
Используется в программном проекте для решения транспорт
ной задачи (задачи коммивояжера). |
Давыдов В.Г, Консольное приложение^ Visual C++ 6
'^/ I
// Включаемый файл программного проекта для решения транс
портной задачи (задачи коммивояжера)
#include "GrHead.h"
280
pMinWayl top2 J.ref = topi; PassWay( top2 ) ;
}
}
return/
}
281
pMinWayf j ], exist = 0;
}
pMinWay [ start ]. exist = 1;
pMinWay[ start ] . SumDist = O.Of;
pMinWay[ start J.ref = -1;
return/
}
282
// Закрытие файла результатов
RetCodel = fclose( pStrOut );
±f( RetCodel == EOF )
{
printf( "\n Ошибка 150. Файл %s не закрыт \n",
pFileOut );
exit ( 150 ) ;
}
retujcn/
283
Достигнутая вершина - отправная точка
пути int InterMediate
PassWay
Финиш пути int finish
Граф GRAPH Gr
Вершина - старт
пути int start Массив с информацией о
solution - • наилучшем пути
Граф GRAPH Gr W *pMinWay
284
что start, Gr, pMinWay являются глобальными объектами
(определены на внешнем уровне). Текст программы, включающий
определение функции solution, приведен выше. Данная функция, в
отличие от предыдущих функций Pass Way и ForStep, является ин
терфейсной функцией и вызывается для решения транспортной за
дачи.
0 0 1 80.0
1 0 3 10.0
2 1 2 20.0
3 1 4 10.0
4 2 3 20.0
5 3 4 10.0
Индексы вершин
1 1 0 0 0 0 exist
pMinWay 0.0 0.0 0.0 0.0 0.0 SumDist
-1 0 0 0 0 ref (REFerence - ссылка):
-1 означает конец списка
start finish
Рис. 92. Состояние массива,pMinWay после начальной подготовки
285
ность пути из start ъ finish.
Индексы вершин
1 1 1 1 1 exist
pMJnWay 0.0 30.0 30.0 10.0 20.0 SumDist
-1 4 3 0 3 ref (REFerence - ссылка):
start finish -1 означает конец списка
i'
286
4. Терминологию при работе с графами.
5. Практическую значимость решения транспортной задачи
(получение оптимального пути между городами, связанными раз
ветвленной системой дорог; определение оптимального маршрута
между заданными пунктами в крупном городе и т.п.).
17. п о и с к
288
фавита). Русские буквы в кодовых таблицах - в общем случае неупо-
рядочены. Отсюда вывод - сравнение ключей поиска, содержащих
латинские буквы, можно проводить непосредственно (например, с
помощью строковой функции strcmp, как в нашем случае). И наобо
рот, если ключ содержит русские буквы, то для сравнения ключей
следует использовать специально написанную для этой цели функ
цию.
Данные для поиска в таблице могут иметь следующий вид:
const ±nt LKEY = 9; / / Длина ключа в строке таблицы
// LKEY~1
// Длина данного в строке таблицы LDATA-1
const xnt LDATA = 63;
289
Key Word (если строка найдена, то ее индекс line, а флаг результата
поиска found=\) или получить ответ, что такой строки в таблице нет
(found=0).
Основными способами поиска в таблице являются.
/ . Последовательный поиск. Эффективность поиска (среднее
число обращений к таблице для нахождения искомой строки) равна
size/2.
2, Логарифмический поиск (бинарный, с помощью двоичного
дерева). Число обращений к таблице равно \Q>%^{size).
J. ПоисКу использующий прямой доступ к таблице. Число
обращений к таблице равно единице.
4. Поиск с использованием перемеиганной, слабо заполнен
ной таблицы (хэт-таблицы). Число обращений к таблице близко к
единице.
Рассмотрим перечисленные способы поиска, кроме малоупот-
ребимого поиска с прямым доступом, рассмотренного в [6].
П
lesson лекция Р
type тип о
с
word слово м
о
size-1 work работа
т
|р|
ключ данное ^-*^
Рис. 97. Пример таблицы для последовательного поиска
290
тивность поиска составляет в среднем size/2 обращений.
Программный проект, в котором содержатся определения
функций для поиска в таблице и пример их использования, приво
дится ниже. В примере на данном этапе следует рассмотреть только
данные и те фрагменты проекта, которые относятся к функции Se-
quentialSearch для последовательного поиска в таблице. К числу та
ких фрагментов относятся, в том числе, функции AllocTableDM
(размещение таблицы в динамической памяти), FreeTableDM (осво
бождение занятой таблицей динамической памяти), SeqlnpTab (за
полнение таблицы), PrintTab (печать содержимого таблицы) и Print-
Search (вывод результатов поиска).
/*
Файл TestSearch.срр.
Тестирование поиска в таблице.
Определение методов поиска в таблице приведено в файле
Sea rch Tab! е. срр.
Заголовочный файл проекта - файл SearchTable. h.
Для откытия и закрытия файлов используются универсальные
функции^ определенные в файлах OpenCloseFile.h и
OpenCloseFile.срр.
Давыдов В.Г. Консольное приложение. Visual C++ 6
*/
291
// Заполняем таблицу для последовательного поиска и
// печатаем ее
SeqInpTab ( ArgV[ 1 ] ) ;
PrintTab( ArgV[ 2 7/ "^"г
" Состояние таблицы:" ) ;
292
retuxm 0;
}
-_
Файл SearchTable.h. Включаемый файл для поиска в таблице.
Давыдов В. Г. Консольное приложение^ Visual C-h+ 6
V
// Предотвращение возможности многократного подключения
// данного файла
Hfndef SEARCHTABLE_H
^define SEARCHTABLE_H
// Прототипы функций
void AllocTableDM( int );
void. FreeTableDM( void );
void SeqInpTab ( char * ) ;
void PrlntTab ( char *pFlleOut, chstr *, char *pHead );
void SequentlalSearch ( char [ ], int <5, int & );
void PrlntSearch(,char *, char *, int, int, char [ ] );
void Round( int );
void InpTabLog ( char * );
void LogarlphmSearch ( char [ ], int &, int & );
int Kod( char );
293
±nt Hash ( chstr [ ] ) ;
void. BeglnTable ( сЬлг *, ±nt ) ;
void. HashSearch ( сЪах: [ ], inb &, inb &),
§endif
// Прототипы функций
FILE * OpenFlle ( char *, char *, inb ) ;
void CloseFlle( FILE *, char *, int WarnNum ) ;
#endlf
// Открытие файла
FILE * OpenFlle ( // Возвращает указатель на структуру
// со сведениями об открытом файле
// Указатель на имя .расширение открываемого файла
сЬаг *pFl 1 eNam е ,
cha.r *pMode, // Указатель на режим открытия файла
// Номер ошибки или предупреждения
int ErrWarnNum )
{
// Указатель на структуру со сведениями об открытом файле
FILE *pStructFlle/
// Открытие файла
pStructFlle = fopen ( pFlleName, pMode ) ;
if( IpStructFlle )
{
294
printf(
"\n Ошибка %d. Ошибка открытия файла %s в режиме \"%s\"\п",
ErrWarnNum, pFileName, pMode ) ;
exit ( ErrWarnNum )/
}
jc&tum pStructFile;
)
// Закрытие файла
void. CloseFile (
// Указатель на структуру со сведениями о закрываемом
FILE *pStructFile,
// Указатель на имя.расширение закрываемого файла
char *pFileNaine^
Int WarnNum ) // Номер предупреждения
{
// Закрытие файла
х£( fclose( pStructFile ) == EOF )
{
printf(
"\n Предупреждение %d. Файл %s не закрыт. \n"
"\n Выполнение программы продолжается \ n " ,
WarnNum^ pFileName );
}
геЬгит;
}
Файл SearchTable.cpp.
Функции поиска в таблице:
* размещение таблицы в динамической памяти и ее
инициа ЛИЗ а ция ;
* освобождение динамической памяти, занятой таблицей/
* заполнение массива значениями, читаемыми из файла на
магнитном диске (для последовательного поиска);
* заполнение таблицы значениями, читаемыми из файла на
магнитном диске (для логарифмического поиска);
* вывод содержимого таблицы в файл на магнитном диске/
* последовательный поиск в таблице;
* вывод результатов поиска в таблице в файл на магнитном
диске;
* обход вершин дерева с целью формирования словаря для
бинарного (логарифмического) поиска;
* бинарный (логарифмический) поиск в таблице, подготовленной
в форме алфавитно-упорядоченного двоичного дерева/
* преобразование символа ключа ~ строчная латинская буква,
цифра или пробел - в его порядковый номер (целое число) ;
* хэш-функция ключа "KeyWord" из "LKEY-1" символа (символ -
строчная латинская буква, цифра или пробел) для таблицы из
size строк;
* начальная подготовка хэш-таблицы;
295
* поиск в хэш-таблице.
Используется в программном проекте для поиска в таблице.
Давыдов В.Г. Консольное приложение^ Visual C-h+ 6 I
JV i
// Включаемый файл программного проекта для поиска в таблице
^include "SearchTable.h"
// Инициализация таблицы
fori ±nt i = О; i < s; 1ч-+ )
{
pTablef i ] . key[ 0 ] = '\0'/
pTablel i ].data[ 0 ] = '\0';
}
size = s;
jretujrn/
296
// Освобождение динамической памяти, занятой таблицей
void FreeTableDM( void )
{
xf( рТаЫе )
{
delete [ ] рТаЫе; рТаЫе = NULL;
}
// Заполнение массива
£ою ( ±nt 1=0; Ksize; i ++ )
{
±f( fscanfi pStructFilelnp, " %s %s",
pTablef i ].key, pTable[ i ].data ) != 2 )
{
printf( "\n Ошибка 40. Ошибка чтения строки^
" таблицы с индексом %d ", i ) ;
exit( 40 ) ;
}
}
// Поиск
1 = 0;
while ( .'found && /EndTab )
{
±£( Istrcmpi Keyword^ pTable [ 1 ] . key ) )
Щ { // Нашли
found = 1; line = 1;
}
else
298
// Шаг вперед по таблице
if( i == size-1 )
{
EndTab = 1/
;
else
{
i ++/
}
}
}
retuim;
}
return/
}
299
// должны быть ал фа витно-упорядоченными по ключам
void Round(
±nt root ) // Корень дерева
{
±£( size < root )
{ // !!! Выход из рекурсии
return/
// Подготовка к поиску
found = 0; EndTab = 0;
// Поиск
i = 1;
while ( ! found && .'EndTab )
{
±f( !strcmp( Keyword, pTable[ i-1 ] . key ) )
{ //Нашли
found = 1; line = i-1;
}
else
{ //Шаг вперед по таблице
±f( strcmp( Keyword, pTable [ i-1 ] , key ) < 0 )
{
i = 2*i/
}
300
else
{
1 = 2*i-i-l/
}
EndTab = (i > size ) ,
return;
301
case '8*: return 34;
case '9': return 35;
case ' ': return 36;
dLefaul t:
printf(
"\n Ошибка 130. Ключ поиска содержит недопустимый символ. \п"
"\п Ключ может содержать толька строчные латинские буквы, "
" цифры и пробел \п" ) ;
exit ( 130 ) ;
}
302
рТаЫе[ i ] . key [ 11 ] = '\0';
fox:( int 12 = 0; 12 < LDATA; 12 + + )
pTablef 1 ] .data[ 12 ] = '\0';
}
// Чтение данного
±f( fscanf( pStructFllelnp, " %s",
pTablef 1 ] .data ) != 1 )
{
printf( "\n Ошибка 160. Ошибка чтения ключа из"
" строки с индексом %d \л", line ) /
exlt ( 160 ) ;
}
303
}
// Поиск В хэш-таблице
void HashSearch(
// Ключевое слово для поиска
char Keyword [ ],
±nt бе founds // 1 - нашли
±nt &11пе ) // Индекс найденной строки в таблице
{
±nt 1, // Индекс строки таблицы
EndTah; // 1 - достигли свободной строки
// Подготовка к поиску
found = О; EndTab = О; i = Hash( KeyWord ) ;
// Поиск в таблице
while ( ( .'found ) && ( .'EndTab ) )
{
±f( pTablel 1 J.keyf 0 ] == ' ' )
{ // Достигли свободной строки
EndTab = 1;
}
else
{
±£( ! strcmp ( pTablef 1 ] . key, KeyWord ) )
{ // Нашли
found = 1; line = i;
}
else
{ // Шаг no таблице
i++; i = ( i > ( size-1 ) ? 0: i ) ;
}
}
}
retujni/
call вызов
type тип
word слово
work работа
304
Тестирование последовательного поиска
Результаты поиска для ключевого слова: and
Строка с ключом "and" в таблице не найдена.
Результаты поиска для ключевого слова: word
Индекс строки в таблице: 2. Найденная строка:
word слово
Состояние та блицы:
word слово
type тип
work работа
call вызов
Тестирование логарифмического поиска
Со стояние та блицы:
call вызов
type тип
Тестирование хэш-поиска
Результаты поиска для ключевого слова: work
Строка с ключом "work" в таблице не найдена.
305
логарифмическом (с помощью двоичного дерева) поиске в таблице.
Если исходную таблицу (словарь) предварительно подготовить
в форме двоичного дерева так, чтобы ключи левого поддерева были
раньше по алфавиту, чем ключ корня, а ключи правого поддерева -
позже, то число обращений к таблице для сравнения с заданным
ключевым словом не может превышать log^isize). При этом, после
каждого обращения к таблице, область поиска сокращается в общем
случае примерно в два раза.
306
вершен.
} D
2
]\
/
/
1
В F н J
А
/
Л- -V
с Е
Последние слова по
10
алфавиту (H-I-J)
присваиваются
Первые слова по правому поддереву
алфавиту (A-B-C-D-E-F)
присваиваются левому
поддереву
Среднее слово по алфавиту (G) присваивается
корню.
307
в соответствии со сказанным, прототип, определение функции
LogariphmSearch и пример ее вызова имеют вид, показанный в про
грамме из подразд. 17.2.
рТаЫе
Keyword Hash(KeyWord)
Индекс строки в
таблице с
Исходный key = Keyword
ключ
Size-1
Ключ (key) Данное (data)
Рис. 99. Хэш-поиск в таблице
308
1. Какую функцию Hash( KeyWord ) следует использовать?
2. Как поступать в ситуации, когда функция Hash{ KeyWord )
не дает местонахождения нужного элемента (! много ключей дают
одинаковый индекс)?
Ответ на второй вопрос заключается в том, что нужно исполь
зовать какой-то метод для получения нового индекса в таблице, а
если и там нет нужного элемента, то следующего индекса и т.д. По
добный случай, когда в строке Hash{ KeyWord ) находится другой
ключ, а не ключ KeyWord^ называется конфликтом, а задача получе
ния альтернативных индексов li^зыв2iQTCЯ разрешением конфликтов.
309
size было простым числом (см. Вирт Н., Алгоритмы + структуры
данных = программы: Пер. с англ. М.: Мир, 1985. С. 305).
310
HashSearch очень похожи друг на друга.
Пример тестирования хэш-поиска в таблице имеется в под-
разд. 17.2 (см. функцию main).
ЗП
18- О Т В Е Т Ы И РЕШЕНИЯ К УПРАЖНЕНИЯМ
Д Л Я САМОПРОВЕРКИ
Ответ к упражнению 1.
retcode^l 1=17 j=123 с1=4 с2=5 сЗ=6 а=2400,000000
Ъ=172,000000
Ответ к упражнению 2.
Файл 2_4_4_2.СРР
float a;
±nt i^ jr
cbSLr cl, c2r c3;
±nt retcode;
ch^jc c4, c5r s[20]
312
FILE *f_in; / / Указатель на файл для ввода
±пЬ ret code; // Возвращаемое значение для функции
// fscanf
z-etiLm О;
Ответ к упражнению 3.
Файл 2_4_4_З.СРР
±nt d = 254/
float f = 1234.56;
cha.r *str = "Строка символов"/
1+254^''^^^''^]^'^[^^''''^254]
(^^^^^1234.5600) ^^ (1234.5600^^'^^^)
/Стр/^^/м/
313
// вывода
±nt main ( void ) // Возвращает О при успехе
{
int d = 254;
float f = 1234.56f;
cha,r *str = "Строка символов"/
FILE *f_out; // Указатель на файл для взвода
// Открываем файл file,out для записи
f_out = fopen( "file.out"г "w" ) ;
±f( f_out == NULL )
{
printf ( "\n Файл file, out для записи не открыт. " ).
return 1;
}
return 0;
Ответ к упражнению 1.
Будет напечатано:
i=l j=3
next ( )=11
last ( )=0
nw(i+j) =9
Ответ к упражнению 2.
Будет напечатано:
i == 3 j = 1
next ( i ) = 3
last ( i ) =10
i = 3 j ^ 2
next ( i ) = 4
last ( i ) = 9
314
18.3. Для подраздела 3.9.3
Ответ купрамснению 1,
Фа ил 3_ 9_3_1. срр
// Прототип
±nt SumUneven( ±nt ar[ ] ) ;
±nt main ( void ) // Возвращает 0 при успехе
{
Int a[ N ] ;
// Инициализация массива
toj: ( ±zib i=0/ i<N; i + + )
a[ i ] ^ 1;
// Вызов функции
±nt s = SumUneven( a ) ;
// Печать найденной суммы
printf( " Сумма значений элементов массива с нечетными "
"индексами = %d \л", s ) ;
x-etujm 0;
}
315
Sum += ar[ i ]/
return Sum/
}
Ответ к упражнению 2.
// Прототип
void. CreateArr ( Int x[ ], int y[ 7, Int z[ ] ) ;
int main ( void ) // Возвращает 0 при успехе
{
±nt x[N],y[N],z[N];
// Инициализация исходных массивов
£or( int i=--0; i<N; i + + )
{
x[ i ] = 1; y[ i ] = 0;
}
// Вызов функции
CreateArr( x, y, z ) ;
retujm 0;
}
// Формирование массива
void CreateArr(
int X [ 7, // Исходные
int y[ ], // массивы
int z[ ] ) // Формируемый массив
{
// Формирование массива
for( int i=0; i<N; i+=2 )
{
if( x[ i ]>y[ i 7 ;
z[ i ] = x[ i ];
316
else
z[ i ] = y[ ± ];
}
Ответ к упражнению 1,
/*
Файл 3_9_3_2, срр
1. В текстовом файле "ctrl4, dat" имеется 15 строк, каждая
из которых имеет следующий формат:
число_ 1 число_ 2
Здесь "число_1" определяет вид геометрической фигуры (1 -
квадрат, 2 - круг) , а "число_2" - параметр фигуры (при "чис-
ло_1" = 1 ~ длина стороны, а при "число_2" = 2 - радиус) .
1.1. Написать определение массива структур для хранения
указанных сведений о геометрических фигурах. Каждый элемент
массива должен иметь следующие поля:
* имя фигуры;
* длина стороны или радиус;
* площадь фигуры.
1.2. Написать фрагмент программы для чтения из файла на
магнитном диске "ctг14.dat" информации о геометрических фигу
рах.
1.3. Написать фрагмент программы, вычисляющий площади
геометрических фигур.
1.4. Написать фрагмент программы, печатающий в файл
"ctrl4.out" параметры геометрических фигур. Сведения об от
дельных фигурах располагаются в отдельной строке и имеют вид:
круг: радиус= . . . , площадь= . . .
или
квадрат: длина стороны= . . . , площадь= . . .
Предусмотреть контроль корректности значений, возвращае
мых функциями библиотеки Си, указать какие включаемые файлы
требует представленный фрагмент.
317
// Определение массива фигур
sbiract GeomFigure
{
cbai: name [ 8 ];// Название фигуры
double pa ram; // Параметр фигуры: длина стороны
// или радиус
double square/ // Площадь фигуры
) агг[ N ]; // Массив геометрических фигур
// Заполнение массива структур со сведениями о
// геометрических фигурах и вычисление их площадей
FILE *f__ln; // Указатель на файл для ввода
// Открываем файл ctrl4,dat для чтения
f__in = fopen( "ctrl4,dat", "г" ) ;
±£( f_±n == NULL )
{
print f ( "\n Файл ctrl4. dat для чтения не открыт. " ) ;
jretuxn 1;
}
±zib Tag; // 1 - квадрат^ 2 - круг
double pa ram; // Параметр фигуры
for( ±nt 1=0; KN; 1 ++)
{
±f( fscanf( f_ln, " %d %lf", &Tag, ¶m ) != 2 )
{
printf( "\n Ошибка чтения " ) ;
return 2;
}
switch ( Tag )
{
ca.se 1:
strcpy ( arr[ i J.name, "Квадрат" ) ;
arr[ 1 ] .pa ram = pa ram;
arr[ 1 ]. square = pa ram "¶m;
break;
case 2:
strcpy( arr[ 1 J.name^ "Круг" ) ;
arr[ 1 ].pa ram = pa ram;
arr[ 1 ].square = 3.141592*param*param;
break;
default:
return 3;
}
}
// Закрываем файл чтения
fclose ( f_in ) ;
// Печать сведений о геометрических фигурах
FILE *f_out; // Указатель на файл для вывода
// Открываем файл ctrl4.out для записи
f_out = fopen( "ctrl4.out", "w" ) ;
lf( f out == NULL )
318
prlntf ( "\n Файл ctrl4. out для записи не открыт. " ) ;
jretujrn 4;
}
fo3z( 1=0/ KN/ 1++)
{
±£( !strcmp ( arrf 1 ] . name, "Квадрат" ) )
{
fprlntf( f_out, "\n Квадрат: длина стороны=%1д, "
"площадь = %1д " , arr[ 1 ] ,param,
arrf 1 ]. square ) ;
}
else
{
fprlntf( f_out, "\n круг: радиус=%1д, "
"площадь=%1д " , arr[ 1 ] .param,
arr[ 1 ]. square ) ;
Нет
Нет
Ответ к упралснению 2.
±f( a<=b )
к=п;
319
r=l;
}
else
r=3;
Ответ к упражнению 3.
swi tab( 1 )
{
case 4:
n-h+ ;
break;
de£ault:
n=a-b;
Ответ к упражнению 4.
Будет напечатано:
•к
-- О -- -1 — -2 -- -3 -- -4
Ответ к упражнению 5.
/*
Файл 4__12_5. срр
5.Пусть определен массив
int а[ 25 ];
Напишите фрагмент Си-программы, который напечатает с но
вой строки значения элементов "а" по 5 элементов в строке и
по 10 позиций на элемент. Решить задачу с помош^ю цикла
while.
В. Давыдов. Консольное приложение (Microsoft Visual Stu
dio C++ 6. 0)
*/
^include <stdio.h> // Для ввода-вывода
320
{
а[ i ] = 1;
}
// Печать массива
± = О;
while( i<25 )
{
±f( !( 1%5 ) )
printf ( "\п" ) ;
printf( "%10d", a[ ± ] ) ;
i ++ ;
}
printf ( "\n" ) ;
z-etux-n 0;
Ответ к упражнению 6.
Ответ к упражнению 7.
Будет напечатано:
10 13 16
15 13 11
Ответ купрамснению 2.
Будет напечатано:
рр-р = 4 *рр-а - 4 **рр = 14
рр-р = 3 *рр-а = 3 **рр = 13
рр-р - • = 4 *рр-а = 4 **рр = 14
рр-р = 4 *рр-а = 3 **рр = 13
Файл LS.CPP
321
Определены следующие данные:
// Прототипы функций
void Create__beg ( EL *&, char * ) ;
void Add_end( EL *&, int ) ; \
void Prlnt_ls ( EL *, char *, char * ) /
void Dest_ls( EL *& ) ;
void Del_beg( EL *& ) ;
322
EL *start;
jretixzm 0;
}
323
int i) // Данные добавляемого элемента
// Указатель на новый (добавляемый) элемент списка
EL *temp,
*сиг; // Указатель на текущий элемент
rBturzi/
}
//***********************************************************
/ / Разрушение списка
void Dest_ls (
EL *&start ) // Указатель на начало списка
{
±f( start == NULL )
{
printf( "\n Список пуст. Удалять нечего" ) ;
return;
}
while( start /= NULL )
Del_beg( start ) ; / / Удаление первого элемента списка
return;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
325
// Удаление первого элемента списка
void Del_beg(
EL *&start ) // Указатель на начало списка
{
EL *del; // Указатель на удаляемый элемент
±f( start == NULL )
{
printf ( "\n Список пуст. Удалять нечего" ) ;
return/
}
// 1: подготовка первого элемента для удаления
del - start;
start = del->next; // 2: start сдвигается на второй
// элемент
delete del; // 1: удаление первого элемента
return;
ПРИЛОЖЕНИЯ
• программирование на ПМ-ассемблере;
Q ввод;
о вывод;
• простейшие ветвления;
а циклы;
а структуры;
Q функции;
о области действия определений;
о массивы и указатели;
о работа с динамической памятью и операции с линейным списком;
а препроцессор, перечисления, функции с умалчиваемыми значениями ар-
гументов, перегрузка функций, шаблоны функций, перегрузка операций.
327
большего элемента массива.
328
Вариант 13, Ввести и напечатать значения переменных х, у и
Z вещественного типа. Вычислить и напечатать значения
переменных и := т а х ( х, у, z ) , / : = min( х, у, z ).
1 при к = min{ X^ у^ Z ) ,
Р := 2 при у = min{ X^ у г Z ) ,
3 при Z = m i n ( X, у г z )
+1 при X > О ^
У := О п р и X = О,
-1 при X < О
329
п. 1.1.2. Ввод в языках Си/С++. Варианты тестов
Вариант 7.
double d; // 4. 7
char s[ 3 ]; // "Ой
unsigned long uli; // 31
short si; // 12
char c; // 'r'
xnt 1; // -21
Вариант^ 2.
long double b; //4.7
char s[ 3 ]; // "Я"
long i; // -1
short j ; //12
Вариант 3.
long double b; // 4.7e2
char s[ 20 ]; // "4"
int i; // 12
unsigned j ; // 0x21
Вариант 4.
double b; //4.7
char s[ 20 ]; // "Отлично'
long int i; // -21
330
unsxgned. long- J/ // 0x12
Вариант 5.
float <a. // 1.5
b ; // 14. 7
±nt if // -21
J/ // 12
char Clr // 'y'
c2. // 'P'
c3 , // 'a '
c4. // '/ '
s[20]; // "Прочитa иная-строка
Вариант 6.
float b; // 14.0
±nt J/ // 12
unstgnedL u; // 21
cbeir c4. // 'P'
s[20]; // "Зимний-вeчер"
Вариант 7.
double d; // 2.0
float a. // 1.5
b; // 12.21
Int i/ // -21
unsigned J/ // 0x12
char Clr // '1 '
c2. // '2'
c3 , // '3'
c4. // '4'
n n It
s[20] ; //
Вариант 8.
Вариант 9.
float a; // 1.5
int ir // 21
J/ // -12
char Clr // 'в'
c2r // 'e'
^1
s[20]; // "Прочмтанная-строка'
Варит чт 10.
float br // 5.0
k; // 15,123
long^ ±nt a; // 27
char Clr // 'B'
s[20] ; // "Строка
332
+ + + + + + + 4-
+ + + + + + +
I I 1 - I 7 I 7 I | - 1 2 | 4 | 3 1 5 | 5 | 7 | \ л |
+ + + + + + + + + -|. + + + -I- +
I \ 2 \ . \ 4 \ e \ 3 \ I I i I 4 I . I 7 I \л|
+ + + + + + + + + + + + + +
I I I 7 I 2 I I I \л|
+ + + + + + +
К а к и е з н а ч е н и я п о л у ч а т п е р е м е н н ы е RetCode, а, b, /, j , cl, c2,
c3?
Вариант 13, И м е е т с я с л е д у ю щ и й ф р а г м е н т С и - п р о г р а м м ы :
float а, Ь/
±nt i, j ;
chsLx: cl, c2, c3;
±nt RetCode;
RetCode = fscanf( stdin, " %o 2%ld %c 5%c %c %f %f " ,
&i, &jr &cl, &c2, &c3, &b, &a ) ;
С т р о к и и с х о д н ы х д а н н ы х в ф а й л е ( п о т о к е С и ) ''stdin" имеют
следующий вид (каждая клетка содержит один символ):
I I I 17 17 1 I I 2 I 4 I 3 I 5 I 5 I 7 I \л|
+ + + + + + + + + + + + + -I- +
I \ 2 \ . \ 4 \ е \ 3 \ I \ 1 \ 4 \ . \ 7 \ \п\
+ + + + + + + + + + + + + +
I I I 7 I 2 I I I \л|
+ + + + + + +
Какие значения получат переменные RetCode, а, Ь, i, j , с 1, с2,
сЗ?
Вариант 14, И м е е т с я с л е д у ю щ и й ф р а г м е н т С и - п р о г р а м м ы :
float а г Ь;
±nt i/ J/
chstr clr s[ 20 ];
333
один символ):
+ + + + + + + + + + + + + + +
I I \ S \ t \ r \ i \ n \ g \ \ 1 \ 2 \ 1 1\л|
I J I . I 5 I I \ - \ 2 \ 1 \ I 4 I . I 7 I \л|
+ + + + + + + + + + + + + +
I I - I . I - I I \л|
+ + + + + + +
Предусмотреть контроль корректности значений, возвра
щаемых функциями библиотеки Си ^^fopen^\ ^^fscanf\ Подклю
чить необходимые стандартные заголовочные файлы.
334
• открытие файла (потока Си) "Input'' для работы с файлом операцион
ной системы "Test2Jn'';
• ввод из этого файла (потока Си) следующих значений указанных ни
же переменных:
а: 1,5 Ь : 4.7
±: -21 j : 0x12
I i 1 . I 3 I I 1 - I 2 I I I I 4 I . I 7 I \л|
+ + + + + + + + + + + + + +
Предусмотреть контроль корректности значений, возвра
щаемых функциями библиотеки Си ^^fopen^\ ^^fscanf\ Подклю
чить необходимые стандартные заголовочные файлы.
335
• ввод из этого файла (потока Си) следующих значений указанных ни
же переменных:
а : 1.5 b : 14.7
1 : 1 j : 12
cl: ' .' s : "Это хорошо"
1 1 1 . 1 5 1 I 1 I 2 I I I 1 . 1 I I \л|
+ + + + + + + + + + + + + +
\ 1 \ 4 \ . \ 7 \ I I I I I ! I I \л|
+ + + + + + + + + + + + + +
Предусмотреть контроль корректности значений, возвра
щаемых функциями библиотеки Си ^^fopen^\ ^^fscanf\ Подклю
чить необходимые стандартные заголовочные файлы.
336
+ + + + + + + + + + + + + + +
I S l M l M l a l - l B l e l ^ J l e l p I \ p \ \ \n\
+ +- - - + + + + + + + + + + + + +
I I 1 I 1 1 1 2 1 I I \ 2 \ 1 \ \ \n\
+ + + -f + + + -I- -f + 4- -I- 4- +
I i I 4 I . I 7 I I I I 1 I I I I \ л |
H + + + + + + H + + + + + +
Предусмотреть контроль корректности значений, возвра
щаемых функциями библиотеки Си ^^fopen^\ ^^fscanf\ Подклю
чить необходимые стандартные заголовочные файлы.
Вариант 1.
floett г;
±пЬ 1 = 17;
г = l,5f * 2.0elf;
fprlntf ( stdout, "*r=%5.2e^%s'^*l = %—i'd\n*%-3s\n".
Вариант 2.
float r;
±nb 1 = 17/
r = 1.5 * 2,Of
fprint f( stdout, "*r^%5.2f^%5s^*l=%-+10d\n*%-30s\n",
r, " _ " , 1, "*", );
Вариант 3.
double r;
int 1 = 17;
r = 1.543 * 2. 0;
fprlntf( stdoutr "*r=%5.21f'^%-4s^-^l = %- + 10d\n*%-8s\n",
Вариант^ 4.
float r = 3.0;
int 1 = 17;
fprlntf ( stdoutr "*r=%5.2f''%5s^*l = %- + 10d\n*%-30s\n",
T^ II n ,• II Tic " ) .
Вариант 5.
float r = 1.5e2;
±nt 1 = 7 ;
fprlntf( stdout, "^%30s\n^r=%f^%5s^l=%10d\n", "*", r ,
" " " , 1 );
Вариант 6.
float r = 1.5e2;
int 1 = 5 ;
fprlntf( stdout, "^r=%f'^%-5s^l = %+10d\n'^%2.3s",
Гг "*", 1, "строка" );
338
Написать фрагмент программы, обеспечивающий:
• Опфытие файла (потока Си) ^^Outpuf^ для работы с файлом операци
онной системы ^^ Tests. ouf\
• Вывод в открытый поток ''Outpuf строк заданного вида.
Указание. При выводе максимально использовать указанные в вариан
тах данные и возможности форматированного вывода.
• Закрытие файла (потока Си).
Предусмотреть контроль корректности значений, возвращае
мых функциями библиотеки Qnfopen п/close. Подключить необхо-
димые стандартные заголовочные файлы.
339
Вид выводимых строк (ниже знак ^ обозначает пробел):
[+254] '^^[''^^^^254]
(^^1234, б) ^-^ (1.234560Е+03)
/^^'-^^^'^^^^Стр/^^ /м/
340
Вариант 15. Фрагмент Си-программы:
±пЬ d = 254;
float, f = 1234.5 6;
char *str = "Строка символов";
341
Вид выводимых строк (ниже знак ^ обозначает пробел):
(123 ;
342
[ 1 при i=l или 2 или 1,
л := [ 2 при 1=10,
[ О в остальных случаях
: > ^ Да
уНет
R:=X; P:=Y;
i к
Q:=1;
R:=Y; Р:=Х:
343
Да
X>Y Q:=1;
Нет
R:=Y; P:=X;
Да
^а<=Ь^ a:=d;
Нет
R:=X; P:=Y;
V
у"^"" a:=d;
Да i L
R:=X; P:=Y;
344
Вариант 17, С помощью операторов ветвлений и присваива
ния записать фрагмент программы, вычисляющий величину i ( i >= О
) по следующему правилу:
[ л+1 при 1=0,
л := [ а+Ь при 1=2,4,6,8,10 и т.д.
[ а-Ь в остальных случаях
•
Рис. 105. Фрагмент схемы программы
345
Вариант 2. Задан массив:
double а[ 104 ];
346
При каких исходных значениях к приведенный ниже цикл бу
дет выполняться бесконечно?
wb±le ( к < 5 ) k+'h;
Возможные варианты ответов: при к <= ..., или при к >= ..., или
таких к не существует.
Возможные варианты ответов: при А: <= ..., или при к >— ..., или
таких к не существует.
347
±nt к;
При каких исходных значениях к приведенный ниже цикл бу
дет выполняться бесконечно?
do
{
к+ + ;
} while ( к > 10 ) ;
Возможные варианты ответов: при к <= ..., или при к >= ..., или
таких к не существует.
Возможные варианты ответов: при к <= ..., или при к >= ..., или
таких к не существует.
348
fojc( к ^ 5; к >= 5; к— ;
(
printf( "\п\п" ) ;
п = 6 - к; printf( "%l--%4d-%2s--", к, л, "-" ) ;
}
Возможные варианты ответов: при к <= ..., или при к >= ..., или
таких к не существует.
{
п = б - к; printf( "%i--%4d-%2s--", к, л, "-" ) ;
}
349
п = 8 - к/ pr±ntf( "%i--%4d-%2s--", к, л, "-12" ) ;
}
350
порядковый номер студента в группе; позиция 3 - пробельная лите
ра; позиции 4...22 - фамилия студента длиной не более 18 символов,
в произвольном месте поля; позиция 23 - пробельная литера; пози
ция 24.- четыре оценки по четырем предметам, разделенные не ме
нее чем одной пробельной литерой. Количество студентов в группе
равно 16. Пример строк указанного файла:
01 Андреев 5 4 5 5
02 Быков 5 5 5 5
16 Яковлев 4 4 5 4
351
следующие поля данных: фамилия студента длиной не более 13
символов, начинающаяся с позиции 1; экзаменационная оценка (в
позиции 16). Между последней литерой фамилии и оценкой распо
ложены пробельные литеры.
16 Яковлев 4 5 4
352
в каждой строке файла "Task4.in" содержатся следующие поля
данных: фамилия студента длиной не более 13 символов, начинаю
щаяся с позиции 1; экзаменационная оценка (в позиции 16). Между
последней литерой фамилии и оценкой расположены.пробельные
литеры.
lianncaTb фрагмент программы, который заполнит экзамена
ционную ведомость ^'Math*^ данными, вводимыми из файла опера
ционной системы "Task4.in" (ввод данных должен осуществляться в
текстовом режиме).
353
chajT fam[ 21 ];// Фамилия экзаменуемого
int mark; // Экзаменационная оценка
} math[ 16 ];
// Абсолютная успеваемость (процент студентов с
// положительными оценками)
£1оа.Ь аи;
// Качественная успеваемость ( процент студентов, получивших
// "4" и "5" )
float ки;
16 сметана 15
354
15 Петров Архиваторы 7.200
05 Кухня 5
16 Петров 4 5 4
355
списка должников (студентов, имеющих хотя бы одну двойку ) в
файл "Task4.ouf\
356
1. Калининский 10 20 7
10. Выборгский 15 10 9
05 Кухня 5
357
''Task4An''\ 2) фрагмент программы, вычисляющей и печатающей
полные данные (номер, автор, название и цена) самой дорогой книги
в библиотеке в файл "Task4.ouf\
16 Петров 4 5 4
358
(целое). Количество продуктов в ведомости равно 16. Пример строк
указанного файла:
01 минтай 20 100
02 щука 21 120
16 сметана 15 150
359
Вариант 7. Подсчитать в одномерном массиве целого типа
размером 100 элементов количество нулевых значений.
360
Вариант 14. Поменять местами первый и последний элемент,
второй и предпоследний и т.д. в одномерном массиве вещественного
типа заданного размера.
361
printf( "± = %d j^%d \ л " , i , j ) ;
{
±nt i =2, j ^ 0;
pr±ntf( "± = %d j = %d \n", 1, j ) ;
{
Izit j = 10; i += 1; j += 2;
printf( "i = %d j=%d \ л " , i , j ) ;
}
printf( "i = %d j=%d \ л " , i , j ) ;
}
printf( "i = %d j = %d \n", i, j ) ;
z-etuzrn 0;
} // end function "main"
^include <stdio.h>
±nt i = 10, j = 2;
±nt main ( void )
{
a u t o ±nt i == 8;
{
±nt j = 0; printfi "i = %d j = %d \ л " , i , j ) .
{
int j = 10; i += 1; j += 2;
printf( "i=%d j=%d \n", 1, j ) ;
}
j + + ; printfi "i = %d j=^%d \n", i, j ) ;
}
printfi "i = %d j = %d \ л " , i , j ) ;
return. 0;
}
^include <stdio.h>
Int i , j = 1;
±nt maini void )
{
±nt i = 5;
{
{
±nt j = 2; j += 3;
}
j += 5; printfi "i+l=%d j=%d \n", i+1, j ) ;
}
printfi "i = %d j = %d \n", i, j ) ;
return 0;
}
362
^include <std±o.h>
±пЬ 1 =^ 1, j = 10;
Inb main ( void. )
(
±nt i = 3;
{
printf( "i + l=%d j=%d \ л " , i+1, j ) ;
{
±nt j = 1; j += 3;
}
j -f- 5; printf( "l=%d j^%d \л", i, j ) ;
}
printfi "i = %d j-i-l=%d \ л " , i , j-hl ) ;
iretuxn 0;
}
Вариант 5. Что напечатает следующая программа?
^include <stdio.h>
int i = 1; j = 10;
±nb main ( void. )
{
int i = 3;
I
printf( "i+l^%d j = %d \n"r i+1, j ) ;
(
int j = 1; j +=^ 3;
}
j += 5; printf( "i=%d j=%d \n", i, j );
}
printf( "i = %d j + l==%d \n'\ i , j+1 ) ;
геЬ\12ПЛ 0;
}
^include <stdio.h>
int i, j ;
int main ( void )
{
auto int i = 3;
{
printf( "i + l=%d j=%d \ л " , i + 1, j ) ;
{
auto int j = 1; j += 3;
printf( "i = %d j = %d \n", i/j ) ;
}
j += 5; printf( "i = %d j = %d \n", i, j ) ;
}
printf( "i = %d j + l = %d \n", i , j+1 ) ;
return 0;
}
363
Вариант 7. Что напечатает следующая программа?
^include <stdlo.h>
±nt 1 = 10^ j /
int main ( void, )
{
static int i = 3/
{
printf( '4 = %d j = %d \n", i, j ) ;
{
a u t o int j = 10; i += 1; j += 2;
prlntf( '4 = %d j = %d \n", 1, j ) ;
}
j += 5; prlntf( "l = %d j = %d \n", 1, j ) ;
}
prlntfi "l = %d j = %d \n", i , j+1 ) ;
return 0;
}
Вариант 8. Что напечатает следующая программа?
^Include <stdlo,h>
int 1=10, j =2;
int main ( void )
{
auto int 1=8;
{
int j = 0; prlntf( "l=%d j=%d \n'\ i, j )
{
int j = 10; 1 += 1; j += 2;
prlntf( "l = %d j = %d \n", 1, j ) ;
}
j++; prlntfi "l = %d j = %d \л", i, j ) ;
}
prlntf( '4 = %d j = %d \л", i, j ) ;
return 0;
^Include <stdlo.h>
// Прототипы функций
int next ( int ) ; int reset ( void ) ;
int last ( int ) ; int naw ( int ) ;
int 1=1;
int main ( void )
{
auto int 1, j ; 1 = reset( ) ;
fori j = 1; 3 <= 2; j++ )
{
prlntf( "\nl = %d j = %d\n", 1, j ) ;
prlntf( "next( 1 ) = %d\n", next( 1 ) ) ;
364
printf( "last( i ) - %d\n", last ( i ) ) ;
prlntf ( "naw( 1+j ) = %d\n", naw ( 1-hj ) ) ;
}
retuxrn 0;
return 1/
return ( j = i ++ ) ;
return 1;
return ( j = 1-- ) ;
365
±nt naw ( ±пЬ i )
{
auto ±nb j - 10; return( 1 - j ~= i ) ;
}
return 1;
return( j = --1 ) ;
^include <stdlo.h>
// Прототипы функций
int next ( int ) ; int reset ( void ) ;
int last ( int ) ; int naw( int ) ;
int 1=3;
int main ( void )
{
auto int i, j ; 1 = reset( ) ;
for( j = 4; j <= 5; j++ )
366
{
print f ( "\nl = %d j = %d\n", i , J ) /
print f( "next ( 1 ) = %d\n"^ next ( 1 ) ) ;
prlntf( "last( i ) = %d\n"r last ( 1 ) ) ;
print f ( "naw( 1+j ) = %d\n" r naw ( 1+j ) ) /
}
jcetum 0;
jretujrn i /
i n t next ( ±nt j )
jcGtuim ( j = - - i ) ;
i n t last ( int j )
i n t naw ( int 1 )
^include <stdio.h>
// Upототипы функций
int next ( int ) ; int reset ( void ) ;
int last ( int ) ; int naw( int ) ;
int i = 6;
int main ( void )
{
368
a u t o ±nt j , i; i = reset ( ) ;
£or( j = 2; j <= 3; j++ )
{
printfC "\ni = %d j = %d\n", i , j ) ;
print f( "next( i ) = %d\n", next ( ± ) ) ;
printf( "last( i ; = %d\n"r last ( i ) ) ;
printf( "naw( i+j ) = %d\n", naw( i+j ) ) ;
}
x-etux-ii 0;
}
±nt reset( void )
I
Int 1=2; зо&Ьихпл i-h-h;
}
int next ( int j )
{
return ( j = ~-i ) ;
}
int last ( int j )
{
static int i = 2; return ( j =- i + + ) ;
}
int naw( int i )
{
auto int j = 7; return( i = j -= i ) ;
}
Вариант 16, Что напечатает следующая программа?
^include <stdio,h>
// Прототипы функций
int next ( int ) ; int reset ( int ) ;
int last ( int ) / int naw( int ) ;
int 1=4;
int main ( void )
{
auto int jr 1 = 1; 1 = reset ( 1%4 ) ;
£or( j = 1; j < 3 ; j++ )
{
printfi "\ni = %d j = %d\n", i , j ) ;
print f( "next( i ) = %d\n", next ( i ) ) ;
printfC "last( 1 ) = %d\n"r last ( i ) ) ;
printf( "naw( i-hj ) = %d\n", naw( i+j ) ) ;
}
return 0;
}
int reset ( int i )
{
return i;
)
int next ( int j )
{
return( j = ++i ) ;
369
;
±nt last ( int j )
{
st&tic int i = 6; jretixm {" j = - - i ) ;
}
int naw( int 1 )
{
auto int j = 3; JoetvLTni i = j -= 1 ) ;
}
^include <stdlo.h>
// Прототипы функций
int next ( int ) ; int reset ( void. ) ;
int last ( int ) ; int naw( int ) ;
int 1 = 4/
int main ( void )
{
auto int J, 1/ 1 = reset( ) ;
£or( j = 2; j < 4 ; j++ )
{
prlntf( "\nl = %d j = %d\n", reset ( ) , j ) ;
print f( "next( 1 ) = %d\n", next ( 1 ) ) ;
prlntf( "last( 1 ) = %d\n", last( 1 ) ) ;
prlntfi "naw( 1+j ) = %d\n", naw( 1+j ) ) ;
}
return 0;
return 1++;
return ( j = i-- ) ;
^Include <stdlo.h>
// Прототипы функций
int next ( int ) ; int reset ( void ) ;
int last ( int ) ; int naw( int ) ;
int 1 = 10;
370
int main ( void )
{
auto int jr i/ i = reset( ) ;
fori j = 2; j < 4; j+ч- )
{
printf( "\ni = %d j = %d\n"r reset ( ), j ) ;
{
static int i = 7/ int j = 10;
prlntf( "\ni = %d j = %d\n", i-h+, j ) ;
}
print f ( "next ( i ) = %d\n", next ( i ) ) ;
printf( "last( i ) = %d\n", last ( i ) ) ;
printfC "naw( i-f-j ) = %d\n", naw( i+j ) ) ;
}
return 0;
int reset ( void )
return( i + 5 ) ;
return( j = i~- ) ;
371
return( i + 5 ) ;
return ( j = i-- ) /
retuim. 1;
return( j = -~1 ) ;
372
п.1.1.9. Массивы и указатели. Варианты тестов
373
Int Index ^ "^Pointer;
£or( Index = 1; Index <= 4; Index+=1 )
{
pr±ntf( " %3d"r Array[ +4-IndexJ );
printf( "\n" ) ;
}
Pointer = Array;
£ox: ( Index = 0; Index <= 2; + + Index )
{
printf( " %3d'\ Pointer[ Index+-h ] );
printf( "\n" ) ;
)
return 0;
374
Вариант 6. Что напечатает следующая программа?
^Include <std±o.h>
±nt Array[ ] = { 0 г 4 г 5 г 2 , 3 } ;
±пЬ main ( void )
{
±nt Index, ^Pointer;
£ог( Index = 0; Index <= 4; Index-i-=2 )
printf( " %3d"r * (Array+Index-h+) );
print f ( "\n" ) ;
Pointer = Array + 1;
£ою( Index = 0; Index <= 3; Index + + )
printf( " %3d"r Pointer[ ч-ч-Index ] ) ;
printf ( "\n" ) ;
ire turn 0;
}
375
^include <stdio.h>
Int Array[ J = { 0 , 4 , 5 , 2 , 3 } /
±nt main ( void, )
{
±nt Index, '^'Pointer;
£or( Index = 0; Index <= 2; Index+=2 )
prlntf( " %3d"r * (Array-fIndex) );
printf( "\n" ) ;
Pointer = Array;
for( Index = 1; Index <= 2; Index++ )
printf( " %3d"r Pointer[ Index ] );
printf( "\n" ) ;
iretuim 0/
}
376
£ою ( р = а, ± = О; р + i <= а + 5; р+-/-, ±++ )
pr±ntf( " %3d", *( ++Р + i ) ) ;
printf( "\n" ) ;
£o:c ( p ^ a + 5; p >= a + 1; p-- )
printf( " %3d", *p— ) ;
printf( "\n" ) ;
retvLirn 0;
^include <stdio.h>
x n t main ( void. )
{
±nt a[ ] = { 10, 11, 12, 13, 14, 15 } , i , *p;
for( p = a+2, i = 0; p + i <= a + 5; p+ + , i + + )
print f( " %3d", * ( p -h 1 ) ) /
printf ( "\n" ) ;
fox:( p==a + 5;p>=a + l; p— ;
printf ( " %3d", *—p ) ;
printf ( "\n" ) ;
return 0;
I
^include <stdio,h>
±пЬ main ( void. )
{
Int a[ ] = { 10, 11, 12, 13, 14, 15 } , i , *p;
fori p == a, i = 0 ; p - h i < = a - h 5 - i ; p+ + , i+-h )
printf ( " %3d", *( p + i + + ) ) ;
printf ( "\n" ) ;
£or ( p = a + 5; p >= a ; p~- )
printf( " %3d", *p— ; /
printf ( "\n" ) ;
jretujm 0;
}
^include <stdio.h>
Int main ( void )
{
int a[ ] ^ { 15, 11, 10, 13, 14, 10 } , i , *p;
£or ( p = a, i = 0;p-hi<=a + 5 ~ i ; p+ +, i + + )
printf ( " %3d", p[ i ] ) ;
printf ( "\n" ) ;
tor( p = a + 5; p >= a ; p -= 2 )
printf( " %3d", *p ) ;
printf ( "\n" ) ;
return 0;
Ъ11
Вариант 16, Что напечатает следующая программа?
^include <stdio.h>
±nt main ( void )
{
±nt a[ 3 J[ 3 J ^ { { 1, 2, 3 } ,
{ 4, 5, 6 Ь
( 7, 8, 9 } };
±nt *pa[ 3 ] = { a[ 1 J, a[ 2 ], a[ 1 ] } /
for( int 1 = 0; 1 < 3 ; i++ )
printf( "%d %d %d\n", a[ 1 ][ 2-i J,
*(*(ач-±) + ! ) , * ( pa [ 1 ] ) ) ;
jretujrn 0;
}
^Include <stdio.h>
±пЬ main( void )
{
±nt a[ 3 ][ 3 ] = { { 1, 2, 3 } ,
{ 4, 5, 6 Ь
/ 7 Й Я ) }'
int *pa[ 3 ] = { a[ 2 ]\ a] 0 ], a[ 2 ] } ;
for( int i = 0; i < 2 ; i++ )
printf( "%d %d %d\n", a[ i ][ 2-i ],
*(*(a + i ) + i ) , * ( p a [ i ] ) ) ;
return 0;
^include <stdio.h>
int main ( void )
{
int a[ 3 ][ 3 ] = { { 1, 2, 3 } ,
{ 4 , 5 , 6 Ь
{ 7, 8, 9 } } ;
int *pa[ 3 ] = { a[ 2 ], a[ 0 ], a[ 1 ] } ;
for( int i = 0; i < 2 ; i++ )
printf( "%d %d %d\n", a[ i ][ 2-i 7 ,
*(*(a + i ) + i ) , ^ ( p a [ i ] ) ) ;
return 0;
}
^include <stdio.h>
int main ( void )
{
378
±nt a[ 3 ] [ 3 ] == { { Ir 2, 3 } г
{ 4, 5, 6 Ь
{ 7, 8, 9 } } ;
±nt *pa[ 3 ] - { a[ 2 ], a[ 0 ], a[ 1 ] } ;
fo2:( int i = 0/ i < 2 ; i++ )
printf( "%d %d %d\n"r a[ 1 ][ 2-i 7 ,
*(*(a + i ) + l ) , ^ ( p a [ l ] ) ) /
return 0;
379
параметров. С целью обработки ошибок предусмотреть кон
троль значений, возвращаемых функциями библиотеки Си
^^fopen^\ ^^fscanf^ и операцией new. Подключить необходимые
стандартные заголовочные файлы.
380
struct Node // NODE: узел линейного списка
{
Node *pLlnk; // Pointer LINK:
// указатель на очередной узел
±nt Info; // INFOrmation: информация
} *start;
381
жится некоторое количество целых чисел, разделенных символами
пробельной группы ( ' ', V , '\«' ).
Написать прототип, определение и пример вызова функции,
которая должна BBCCTJI ИЗ файла ''TestS.in'' содержащиеся в нём це
лые числа и запомнить их в узлах линейного списка, в котором каж
дый узел (динамически размещенная в памяти структура) имеет тип
Node. При этом первое прочитанное число должно находиться в
первом от начала узле линейного списка, второе число - во втором
узле и т.д.
Все исходные данные (указатель на "имя. расширение**
файла ввода) и все результаты работы функции (указатель на
начало линейного списка) должны передаваться через список
параметров. С целью обработки ошибок предусмотреть кон
троль значений, возвращаемых функциями библиотеки Си
^^fopen^\ ^^fscanf^ и операцией new. Подключить необходимые
стандартные заголовочные файлы.
382
float Info; // INFOrm at ion: информа ция
I *start;
383
для удаления из списка к первых элементов с освобождением заня
той ими памяти. В частном случае, перед вызовом этой функции ли
нейный список может быть пуст или может содержать любое коли
чество элементов.
Все исходные данные (указатель на начало линейного спи
ска, количество удаляемых элементов) и результаты выполне
ния функции (указатель на начало линейного списка) должны
передаваться через список параметров.
384
Вариант 13. Определен следующий указатель на начало ли
нейного списка:
stxract Node // NODE: узел линейного списка
{
Node *pLink; // Pointer LINK:
// указатель на очередной узел
±nt Info; // INFOrmation: информация
} *start;
385
Node *pL±nk; // Pointer LINK:
// указатель на очередной узел
±nt Info; / / INFOrm at ion: мн ф орма ция
} *start;
386
ного списка) должны передаваться через список параметров.
387
stmict Node // NODE: узел линейного списка
{
Node *pLink; // Pointer LINK:
// указатель на очередной узел
xnt Info; // INFOrmation: информация
} *start;
388
Вариант 3. Функции с умалчиваемыми значениями пара
метров. Имеется следующий фрагмент программного кода:
voxd. DrawCircle ( izit к=100, Int у=100, ±nt radius=100 ) ;
389
struct CMP // CoMPlex: комплексный тип
{
double r; // Вещественная часть
double ±; // Мнимая часть
} ;
390
Является ли запись прототипа функции правильной (обоснуйте
Ваш ответ)? Являются ли правильными приведенные ниже вызовы
функции? В случае положительного ответа укажите, с какими зна
чениями параметров функция будет выполняться?
Rect ( ) ;
Rect ( 2.0 ) ;
Rect ( 2.00, 3.00 ) ;
391
зова для типов int и double.
392
граммных проекта:
• решение простой задачи с использованием ПМ-ассемблера (выпол
няется по усмотрению преподавателя и требует наличия компакт-
диска, прилагаемого к данному учебному пособию);
• структурное программирование средствами языков Си/С++;
• средства модульного программирования в языке C++.
Содермсание отчета
1. ТЕХНИЧЕСКОЕ ЗАДАНИЕ - формулировка решаемой зада
чи, требования к программному проекту, язык программирования.
2. ТЕКСТ ПРОГРАММЫ - назначение программы, листинг с
исходным текстом программы в самодокументируемой форме.
Многочисленные примеры оформления исходных текстов ПМ-
программ имеются в [1] и на компакт-диске.
3. ОПИСАНИЕ ПРОГРАММЫ - назначение программы; метод
решения задачи и основные расчетные соотношения; схема про
граммы с необходимыми пояснениями, выполненная в соответствии
с действующими стандартами.
3. ПРОГРАММА И МЕТОДИКА ИСПЫТАНИЙ - разработка
контрольного примера (примеров) с их обоснованием и анализом,
результаты вычислений по отлаженной программе, выводы.
393
Вариант 7. Ввести и напечатать значения элементов массива
вещественного типа с заданной размерностью. Вычислить и напеча
тать произведение положительных элементов массива. Если массив
не содержит элементов с положительными значениями, то в качест
ве ответа напечатать "В массиве нет положительных элементов".
394
Вариант 15, Ввести и напечатать значения элементов массива
вещественного типа с заданной размерностью. Упорядочить массив
по возрастанию значений элементов. Отсортированный массив на
печатать.
395
временную и широко распространенную среду программирования
Microsoft Visual Studio C++ 6.0.
Формулировка решаемой задачи, С использованием средств
структурного программирования языков Си/С++ спроектировать три
элементарных программы для решения.
1. Задачи с линейным следованием операторов. Например, вы
числить значение функции
у = arctg( 1 1 4 • 1п(«))
Содерлсание отчета.
1. ТЕХНИЧЕСКОЕ ЗАДАНИЕ - формулировка решаемой задачи,
требования к программам, язык программирования.
2. ТЕКСТ ПРОГРАММЫ - для каждой программы в заголовке-
комментарии указать ее назначение, привести листинг с исход
ным текстом в самодокументируемом виде. Создание программ
ного проекта рассмотрено в приложении П.2. Рекомендации по
структуре программы и пример оформления исходного текста
программы приведены в приложении П.З.
3. ПРОГРАММА И МЕТОДИКА ИСПЫТАНИЙ - разработка кон
трольных примеров с их обоснованием и анализом, результаты
вычислений по отлаженной программе, выводы. Рекомендации по
методике отладки разработанной программы приведены в
приложении П.4.
396
fa+b при х<\.
у = } ab при 1<=:X<=2,
Уа-b при х>2
11 6 , 7 J C + 9 , 2 X ^ - 1 , 0 2 O C ^ «рм jc<=0,
•^ » — ^ при x>0
ax +b-x •sin(A:)
_ s'm(x^+x~^+x^^^) \0~^'k
397
f а-х+4 при x>4,
у = Ja(l-e-^) при 0<=х<=4,
I 0 в остальных случаях
У = Е{(-1Г/(2-а+1)}
а=0
Вариант 6, В ы ч и с л и т ь з н а ч е н и я ф у н к ц и й и с у м м у р я д а
sin'^(^2,8-e^+x) V-2 ^^ ^ л
У = •9,110^
х^-а"
Са+Ь при а>Ь,
Д' = при а<-Ь и а>0,
" в остальных случаях
[о
1 V^2,3
а=1 а!
Вариант 7. В ы ч и с л и т ь з н а ч е н и я ф у н к ц и й и с у м м у р я д а
у = Z(2.a-l)-0,5 а-\
а=1
Вариант 8. В ы ч и с л и т ь з н а ч е н и я ф у н к ц и й и с у м м у р я д а
у = 2+ I {Ы)'''^^<-;г^+~^)}
а=0 Ъа-\-\ 2'а+2
Вариант 9. В ы ч и с л и т ь з н а ч е н и я ф у н к ц и й и с у м м у р я д а
a+b
У =
7г/4-\-х' -1/J
398
(a+b+c при \b\<=\a\ и \c\<=\b\.
у = J a+b при \b\<=\a\ и
к1>1*|.
в остальных случаях
[ -
у = \\+ П(2-а+1)
зГ в остальных случаях
х+ух
у = Z(-l)"+'-l/a
У = Z(-l)''-l/(2-f3-a)
а=0
399
Вариант 13. Вычислить значения функций и сумму ряда
с b а
(\g{a/{a+b) при (а'Ь+с)>5,
У = < sin(a) при 0<=(а'Ь+с)<=5,
в остальных случаях
I 0
2 2
X-Z при х>0 и X >Z ,
2 2 2
У = ^ X 'Z при х<0 и X >Z ,
0 в остальных случаях
у = иК2-а+\Г
а=0
при л:>0.
у =
1"
\' при
в
У = Z^^
остальных
-l<=jc<=0.
случаях
?'^'^*^\-Ъ'Б\Г?{Х) , , In(a^)
а""}!^ 24,61-10"
при jc>0.
400
У=<
1"
\' при
в
-1<=л:<=0,
остальных случаях
п
Вариант 17. В ы ч и с л и т ь з н а ч е н и я ф у н к ц и й и с у м м у р я д а
^.(^-)2-a.(,_5)3.sm(^.10-5
{sin(x)
1
при
при х=0,
х>0,
п
ij—x в остальных случаях
а=\
е~^'^+] х^ . ^ - 4
\%{х 1{а-\-Ь)) при (сг+^))>0,
^ 1 \а+6|-lg(jc) при (а+Ь)<=0
у = Е8-(2.а-1)
а=1
^а-х
при х>3.
У =
гf''U при
при
1<х<=3,
л:<=1
й=1 2-0+1
1п*(х)
г a-x+4 при х>4.
у = \ а(1-е-^) при 0<=д:<=4,
в остальных случаях
1 0
401
o^l ^'
402
является использование модульного программирования, в рамках
которого студент осваивает методологию нисходящего иерархиче
ского программирования, в соответствии с которой обоснованно
проектирует файловую и функциональную структуру программного
продукта. Другой важной особенностью программного проекта яв
ляется изучение и практическое освоение методики отладки про
граммных проектов.
Рекомендации по созданию программного проекта приведены
в приложении П.5.
Содержание отчета.
1. ТЕХНИЧЕСКОЕ ЗАДАНИЕ - формулировка решаемой зада
чи, требования к программе (в том числе та часть спецификации,
которая относится к обработке ошибок и предупреждений), язык
программирования.
2. ТЕКСТ ПРОГРАММЫ - для программы в заголовке-
комментарии указать ее назначение, привести листинг с исходным
текстом в самодокументируемом виде. Пример оформления исход
ного текста программы приведен в приложении П.5.
3. ОПИСАНИЕ ПРОГРАММЫ - описание файловой и функ
циональной структур программного проекта (вторая часть
спецификации), краткое описание работы программы и схемы 2-3
функций, выполненные в соответствии с действующими стандарта
ми. 4. ПРОГРАММА И МЕТОДИКА ИСПЫТАНИЙ - описание
методики отладки, требования к контрольным примерам, разработка
контрольных примеров с их обоснованием и анализом, результаты
вычислений по отлаженной программе, выводы.
403
построить вектор длиной (2Л^-1), элементы которого - максимумы
элементов диагоналей, параллельных главной, включая главную
диагональ.
404
дательные, в таком же порядке, как в исходном векторе.
405
• простейшие ветвления;
а циклы;
• структуры;
а функции;
• области действия определений;
• массивы и указатели;
• работа с динамической памятью и операции с линейным
списком;
• препроцессор, перечисления, функции с умалчиваемыми
значениями аргументов, перегрузка функций, шаблоны функций,
перегрузка операций.
Комплексная проверочная работа рассчитана на 1 ч. 15 мин.
Ответ на каждый тестовый вопрос, в зависимости от правильности и
полноты, оценивается О, 0,25, 0,5, 0,75 или 1 баллом. Таким обра
зом, максимальная сумма баллов может достигнуть 5.
В соответствии с набранными баллами выставляются следую
щие экзаменационные оценки:
• "отлично" (4,25-5 баллов);
• "хорошо" (3,5-4 балла);
• "удовлетворительно" (2,5-3,25 балла);
• "неудовлетворительно" (менее 2,5 баллов).
Примеры формулировок тестовых экзаменационных вопросов
содержатся в подразд. П.1Л.
406
1.1. Написать объявление массива структур для хранения ука
занной ведомости.
1.2. Написать фрагмент программы, который заполнит экзаме
национную ведомость данными, вводимыми из файла операционной
системы "Task4.in". Ввод данных должен осуществляться в тексто
вом режиме.
1.3. Написать фрагмент программы, который вычисляет сред
нюю экзаменационную оценку по всем предметам и студентам (т.е.
среднюю оценку из 64 оценок), а затем выводит значение этого по
казателя в файл операционной системы ''Task4.ouf\
Примечание.
Закрыть открытые файлы, как только они станут не нуж
ны.
Предусмотреть контроль корректности значений, возвра
щаемых функциями библиотеки Си ^^fopen^\ ^^fscanf\ Указать,
какие включаемые файлы требует представленный фрагмент.
xnt Array[ ] = { 0 , 4 , 5 , 2 , 3 } ;
Pointer = Array + 1;
fox:( Index = 0; Index <= 2; )
printf( " %3d". Pointer[ ++Index ] );
printf ( "\n" ) ;
return 0;
407
4. Операции с линейным списком. Работа с динамической
памятью. Определен следующий указатель на начало линейного
списка:
stJTuct Node // NODE: узел линейного списка
{
Node *pLink; // Pointer LINK:
// указатель на очередной узел
double Info; // INFOrmation: информация
} * start;
408
Проекты (Projects). Проекты IDE характеризуются следую
щими особенностями.
1. Единицей работы IDE является проект. Проект — это ком
плект файлов.
2. Виды файлов в составе проекта:
• исходные файлы, написанные программистом {*.срр — С Plus Plas
— тексты на языке 0 + + и *./; — Header — заголовочные файлы), IDE
содержит инструменты, которые позволяют автоматизировать со
ставление исходных файлов;
• служебные файлы, которые автоматически создаются IDE, но по
инструкциям программиста.
3. Каталог проекта. Служебные файлы обязательно располага
ются в этом каталоге. Исходные файлы хотя и могут располагаться
где угодно, но, чтобы не запутаться, их тоже следует поместить в
каталог проекта.
4. Проекты IDE и проекты программного обеспечения. Про
стые программы представляют собой просто один проект IDE.
Сложное программное обеспечение реализуется в виде некоторого
множества проектов IDE.
Редактор Символичес
Компилятор Компоновщик
текстов (Text кий отладчик Tools
СИ/С++ (Linker)
Editor) (Debugger)
Рис. 106. Интегрированная среда проектирования программ
MS Visual Studio С-ь+ 6.0
409
Это можно сделать, набрав путь вручную, или воспользовавшись
расположенной справа кнопкой Browse... (просмотр). Разумеется,
что соответствующий подкаталог должен быть предварительно соз
дан.
• Указать утилите Project имя файла проекта. Одновременно
с вводом имени проекта в поле Project Name (имя проекта) это же
имя автоматически добавляется в качестве подкаталога в поле Loca
tion.
После выполнения указанных действий для создания проекта
следует нажать кнопку [ОК], в результате чего на экране появится
диалоговое окно мастера создания консольного приложения. В этом
окне выбираем переключатель An empty project (пустой проект).
При этом создаются только служебные файлы проекта. Для того
чтобы наполнить созданный проект, необходимо добавить в него
файл(ы), содержащий(ие) текст программы.
Это можно сделать двумя способами.
1. Добавить в проект уже существующий файл(ы), создан-
ный(ые) ранее в текстовом редакторе и имеющий(ие) расширение
*.с/?/?. Повторно обращаем внимание на то, что следует предвари
тельно поместить существующий(ие) файл(ы) в каталог проекта
(лучше все иметь в одном месте).
2. Создать новый файл и вставить его в проект.
Добавление в проект существующего файла. Для этого необ
ходимо выбрать в меню Project пункты Add to Project (добавить в
проект) и Files.... В результате этих действий на экран будет выве
дено диалоговое окно Insert Files into Project (добавление файлов в
проект). Здесь следует выбрать те файлы, имеющие расширение
.срр, которые хотите включить в проект. Это можно сделать, либо
дважды щелкнув кнопкой мыши на имени файла, либо выделив
нужные файлы и нажав кнопку [OKJ.
Создание нового файла и включение его в проект. Для созда
ния нового файла необходимо из меню File выполнить команду
New... и в появившемся диалоговом окне New выбрать вкладку
Files, где представлены все типы файлов, которые можно создавать.
Флажок Add to Project (добавить в проект) должен быть установ
лен, чтобы создаваемый файл автоматически был добавлен в проект.
В списке Files выберите тип создаваемого файла — C/C++ Header
File или C++ Source File, а в поле File name: - имя файла. Осталось
нажать кнопку [ОК]. В результате Visual C++ создаст файл и откро
ет пустое поле редактирования текста.
После набора и сохранения всех текстов можно переходить к
следующему этапу - отладке программного проекта.
410
Открытие для работы существующего проекта. Для суще
ствующего проекта необходимо из меню File выполнить команду
Open Workspace ... и в появившемся диалоговом окне Open Work
space войти в каталог проекта и "кликнуть" по файлу с расширением
.dsw. В результате проект загружается в IDE для последующей рабо
ты.
411
Приложение П.З. Рекомендации по структуре
однофайловой программы с одной функцией
и пример оформления исходного текста
Файл TASK01.CPP
412
retvLm 10;
}
413
"\n Значение функции: с=%1'\ с ) ;
// Закрываем файл для записи
±f( fclose( f_out ) == EOF )
{
printf( "\n Ошибка 10. Файл taskOl.out не закрыт \n" ) ;
r&tuxm 10;
}
return 0;
414
шое внимание. Как же вести отладку программы?
Все обнаруживаемые в программе ошибки можно разделить на
три большие категории.
1. Синтаксические ошибки, которые автоматически выявляют
ся на этапе компиляции. Уяснить смысл синтаксических ошибок и
устранить их достаточно легко, так как здесь в качестве достаточно
хорошего помощника выступает компилятор. В зависимости от язы
ка программирования, компилятор лучше или хуже выявляет такие
ошибки. В ряде случаев синтаксическая ошибка в программе влечет
за собой неадекватную реакцию компилятора. Например, отсутствие
скобки часто приводит к тому, что компилятор обнаруживает ошиб
ку через десятки строк кода. В последнем случае можно рекомендо
вать одновременный набор открывающей и закрывающей скобок
(например, { }) с последующим вводом текста между ними.
2. Логические (часто их также называют алгоритмическими)
ошибки. Их бывает наиболее трудно обнаружить и исправить. Часть
из них выявляется на этапе отладки, часть на этапе сопровождения,
а некоторые приводят к тяжелым последствиям.
3. Информационные ошибки. В частности, к Появлению ин
формационных ошибок может привести отсутствие обработки оши
бок ввода-вывода, попытки деления на ноль, переполнение разряд
ной сетки компьютера и т.п. Для исключения и/или обработки ин
формационных ошибок в ряде случаев приходится значительную
часть исходного кода программы отводить для всевозможных про
верок.
415
этого в IDE MS Visual Studio C++ 6.0 командами Build | Build
имя_файла эквивалентно <F7> или Build | Rebuild All. Единствен
ным отличием этих команд является то, что команда Rebuild All не
проверяет даты создания файлов проекта и компилирует все файлы,
а не только те, которые были модифицированы после компиляции.
Аналогичным образом, в IDE Borland C++ 3.1 можно использовать
команды Compile | Маке имя_файла эквивалентно <F9> или Com
pile I Build All. В результате создается исполняемый файл с расши
рением .ехе,
• Можно сразу запустить приложение, выполнив в IDE MS
Visual Studio C++ 6.0 команду Build | Execute имя_файла или по
комбинации клавиш <Ctrl+F5>, а в IDE Borland C++ 3.1 -команду
Run I Run имя_файла или по комбинации клавиш <Ctrl+F9>. Если
в программный проект были внесены какие либо изменения, то в
IDE MS Visual Studio C++ 6.0 на экране будет высвечено диалоговое
окно с запросом на построение исполняемого файла. Для построе
ния указанного файла следует нажать кнопку [Да]. В IDE Borland
C++ 3.1 подобный запрос не выполняется.
Если программный проект содержит синтаксические ошибки,
то в IDE MS Visual Studio C++ 6.0 при выполнении любой из пред
ставленных команд информация об ошибках автоматически отобра
жается во вкладке Build окна Output, по умолчанию расположенно
го в нижней части окна IDE. Если окно Output было удалено с экра
на, то его можно вывести снова на экран с помощью команды View |
Output или по комбинации клавиш <Alt+2>. Каждое сообщение об
ошибке или предупреждении начинается с имени файла, где они об
наружены, за которым следует номер строки, где это произошло, а
далее идут двоеточие и слово "error" (ошибка) или "warning" (пре
дупреждение) и соответствующий номер. В конце приводится крат
кое описание ошибки или предупреждения. Если дважды щелкнуть
левой кнопкой мыши на строке с сообщением или предупреждени
ем, то ошибочная строка будет отмечена стрелкой в левой части в
соответствующем окне редактирования. Лучше всего добиться, что
бы в окончательном варианте не было ни того, ни другого, хотя с
предупреждениями исполняемый файл создается и может быть за
пущен.
Аналогичным образом, в IDE Borland C++ 3.1 при наличии
синтаксических ошибок при выполнении любой из представленных
выше команд информация об ошибках автоматически отображается
в появившемся окне Message. Каждое сообщение об ошибке или
предупреждении начинается со слова "error" (ошибка) или
"warning" (предупреждение), за которым следуют имя файла и но
мер ошибочной строки, а далее идут двоеточие и приводится крат
кое описание ошибки или предупреждения. Если дважды щелкнуть
416
левой кнопкой мыши на строке с сообш^ением или предупреждени
ем, то в соответствуюш[ем окне редактирования в ошибочную строку
будет помещен курсор, а текст сообщения будет повторен в нижней
части окна редактирования.
После устранения синтаксичесих ошибок следует запустить
программу, выполнив команду Build | Execute имя__файла либо по
комбинации клавиш <Ctrl+F5> (IDE MS Visual Studio C++ 6.0) или
команду Run | Run имя_файла либо по комбинации клавиш
<CtrI+F9> (IDE Borland C++ 3.1). При этом также можно получить
сообщение об ошибке (или ошибках). Это тот самый случай, когда
программный проект не содержит синтаксических ошибок, а при
ложение не работает. Вызвано это так называемыми логическими
(алгоритмическими) ошибками, для обнаружения которых можно
использовать разные методы (например, закомментировать фраг
менты программы).
Однако лучше всего воспользоваться имеющимся в IDE
встроенным отладчиком.
417
ш^ие первой точке останова, выполняются правильно. Самый про
стой способ установки точки останова заключается в следующем.
Курсор устанавливается на строку, на которой нужно остановить
работу программы, и нажимается клавиша <F9>. Повторное нажатие
клавиши <F9> удаляет точку останова. Строка останова в окне ре
дактирования отмечена темно-красным кружком в крайней левой
позиции. Если, после задания точки останова, программу запустить
по команде Build | Start Debug | Go, либо нажав клавишу <F5>, то
все операторы программы, предшествующие точке останова, будут
выполняться в обычном режиме и только перед строкой останова
выполнение программы приостановится.
При этом внешний вид интегрированной среды разработки
существенно изменится. Во-первых, изменипся состав основного
меню. Во-вторых, строка, которая будет выполняться следующей,
будет отмечена желтой (по умолчанию) стрелкой. И, наконец, поя
вится два новых окна - Variables (переменные) и Watch (наблюде
ние), которые позволяют просматривать и менять значения пере
менных. Если одно из окон или оба окна на экране отсутствуют, то
их можно поместить на экран, используя комбинации клавиш
<Alt-4-3> для переменных и/или <Alt+4> для наблюдения.
Для пошагового выполнения в отладчике имеются следующие
команды.
• Debug I Step Over (шаг через) или эквивалентно <F10> -
выполняет текущий оператор или функцию и переходит к следую
щей строке.
• Debug I Step Into (шаг внутрь) или эквивалентно <F11> -
выполняет текущий оператор языка C++ или переходит к первому
оператору функции.
а Debug | Step Out (шаг вне) или эквивалентно < S h i f t + F l l > -
завершает выполнение текущей функции и переходит к строке, не
посредственно следующей за ее вызовом.
• Debug I Run to C u r s o r (выполнить до курсора) или эквива
лентно <Ctrl+F10> - выполняет программу до строки, где в текущий
момент находится курсор.
В окне Variables (переменные) автоматически отображаются
только локальные переменные текущего блока. После каждого шага
выполнения программы значения этих переменных обновляются. В
строке Context указывается, в какой функции (блоке) в данный мо
мент находимся.
Переменные, которые нужно контролировать или изменять по
желанию программиста, можно задать в окне Watch (наблюдение).
Для этого в свободной строке столбца Name для контроля значения
переменной достаточно набрать идентификатор переменной и на-
418
жать клавишу [Enter]. Для изменения значения переменной в про
цессе отладки следует выбрать строку с именем этой переменной, с
помощью клавиши [Tab] перейти в столбец Value, набрать там но
вое значение и нажать клавишу [Enter]. При дальнейшей отладке,
вместо прежнего значения, будет использовано модифицированное
таким образом значение переменной.
Для просмотра значения переменной в реэюиме отладки, наря
ду с использованием окон Variables и Watch, можно в окне редак
тирования поставить курсор на имя интересующей нас переменной.
Если переменной было присвоено значение, то появится всплываю
щее окно со значением этой переменной Эта возмолсностъ наибо
лее удобна и мы рекомендуем ее использовать как моэюно чаще.
419
П.4.3. Тестирование программного проекта
420
функции программного проекта (в них следует проверить
результаты работы функции);
• если декомпозиция задачи выполнена не очень удачно и функция
получилась большой (более страницы текста), то следует в ее теле
выбрать промежуточные точки останова, разбив тело функции на
функционально законченные части;
• если функция была отлажена ранее, то после нее точку останова
выбирать не следует;
• если функция результаты своей работы выводит в файл на маг
нитном диске, на экран или на принтер, то после такой функции
точки останова тоже не нужны.
421
его спецификации. Спецификация программного проекта включает
требования к обработке ошибок и предупрелсдений, а такэюе све
дения о файловом и функциональном составе программного проек
та и о взаимодействии функций проекта друг с другом.
Прежде всего рассмотрим, что же понимается под предупреж
дениями и ошибками. Предупреэюдение необходимо выдавать при
наступлении некоторого события, которое требует информирования
пользователя, но не препятствует продолжению работы программы.
Ошибка возникает при наступлении события, когда дальнейшая ра
бота программы невозможна. При обработке ошибок и предупреж
дений для каждого предупреждения или сообщения об ошибке в на
чале диагностического сообщения следует напечатать номер преду
преждения или сообщения об ошибке, обеспечив нумерацию в воз
растающем порядке. Предупреждения, как правило, следует выда
вать в файл результатов, а сообщения об ошибках - на экран.
422
Файл Main.cpp Файл ErWarnW.cpp Файл CheckCS.cpp
ErrorWarningWork()
main()
Обработка ошибок и
Главная функция
предупреждений
Proglnfo()
CheckComString()
Информация о прог
Контроль командной
рамме и командной
строки
строке
/ / Прототипы функций
void ReadArray(
±nt Arr[ N ], // Вводимый массив
// Файл ввода
СЪАГ Filelnp[ ] ) ;
void. PrlntArray (
int Arr [ N ] r // Выводимый массив
// Файл вывода
char FileOutf 7,
cha.r Mode [ ] r // Режим открытия файла вывода
423
/ / Заголовок для печати
char Header[ ] ) ;
424
±nt ErrCode ) // Код ошибки
(
±f( ArgC != 3 )
{
printf(
"\n Ошибка %d. Непредусмотренный формат командной строки. "
" \ л Для запуска программы используйте командную строку вида:"
"\п Т4сполняемый_файл Файл_ввода Файл_вывода'\ ErrCode ) ;
e x i t ( ErrCode ) ;
}
// Прототип
•vo±(X ErrWarnWork ( xnt ErrWarnCode, сЬат Msg[ ] , char Type^
cbar FileOutl ] = "", сЬаг Mode[ ] = "" ) ;
// Определение
void ErrWarnWork(
// Код ошибки или предупреждения
±nt ErrWarnCode,
char Msg[ ], // Строка сообщения
char Type, // 'е' - ошибка, сообш,ение выдается
// на экран, 'w' - предупреждение,
// сообш,ение выдается в файл на МД
// Файл вывода
char FlleOutf ],
cha.r Mode [ ] ) // Режим открытия файла вывода
swibch( Туре )
{
ca.se 'е' :
case 'w*:
cLefavLl Ь:
printf(
425
"\п Ошибка %d. Использован недопустимый тип сообщения:"
"\п используйте \'е\' или \'w\' ", ErrWarnCode ) /
exit( ErrWarnCode ) ;
}
retvLrn;
}
426
тия файла.
427
8. Ошибка чтения значения элемента матрицы.
При возникновении данной ошибки программа прерывает ра
боту, выдавая на экран следующее сообщение:
"Ошибка № XX. Ошибка чтения значения элемента матрицы с номе
ром строки ... и номером столбца ... из файла <имя.расширение>. "
Код ошибки и код возврата задаются в вызове функции обра
ботки предупреждений и сообщений об ошибках.
428
расположенными в отдельных файлах
jcebvLxrn. О;
} // Конец файла TASK02.CPP
429
Файл TASK02.H
Включаемый файл для проекта TASK02.PRJ: содержит стандарт-
ные включаемые файлы и прототипы функций.
Файл TASK02_1.CPP
Чтение исходных данных из файла TASK02.DAT. Используется в
поограммном проекте TASK02.PRJ
V
// Стандартные включаемые файлы и прототипы функций
^include "task02,h"
void ReadData(
float &Lenght, // Длина садового участка
float &Width ) // Ширина садового участка
{
FILE *f__in; // Указатель на структуру со
II сведениями о файле для чтения
// Читаем данные
±f( fscanf( f__in, " %f %f", &Lenght, &Width ) != 2 )
(
printf( "\n Ошибка 20. Произошла ошибка чтения из"
" файла task02.dat \п" );
exit ( 20 );
}
430
±f( fclose ( f_in ) == EOF )
{
printf( "\n Ошибка 30. Файл task02.dat не "
"закрыт \n" ) ;
exit ( 30 );
}
iretuirn/
// Конец файла TASK02_1.CPP
/*
Файл TASK02 2.CPP
Печать исходных данных в файл TASK02.RES. Используется в
программном проекте TASK02.PRJ
void WrlteDat(
float Lenght^ // Длина садового участка
float Width ) // Ширина садового участка
(
FILE *f_out/ // Указатель на структуру со
// сведениями о файле для записи
return/
} // Конец файла TASK02 2.GPP
Файл TASK02_3.CPP
Вычисление плош,ади прямоугольника. Используется в про-
граммном проекте TASK02.PRJ
431
// Стандартные включаемые файлы и прототипы функций
iinclude "task02.h"
void. Area (
float Lenght, // Длина садового участка
float Widths // Ширина садового участка
float & Square ) // Площадь садового участка
{
retxum;
} // Конец файла TASK02_3.CPP
__
Файл TASK02_4.CPP
Печать результатов в файл TASK02. RES. Используется в про
граммном проекте TASK02.PRJ
*/
/ / Стандартные включаемые файлы и прототипы функций
^include "task02.h"
void WriteRes(
float Square ) // Площадь садового участка
{
FILE *f_out; // Указатель на структуру со
// сведениями о файле для записи
// Открываем файл для дозаписи
±f( ( f_out = fopen( "task02.res", "a" ) ) == NULL )
' {
printf( "\n Ошибка 60. Файл task02.res для дозаписи"
" не открыт \п" ) ;
exit ( 60 ) ;
}
// Закрываем файл
±f( fclose ( f_out ) = - EOF )
{
printf( "\n Ошибка 70. Файл task02.res не"
" закрыт \n" ) ;
exit ( 70 ) ;
}
return;
} // Конец файла TASK02 4.CPP
432
в приведенном примере рекомендуем.
1. Обратить внимание на оформление заголовочного файла и
его подключение к файлам проекта.
Повторим здесь еще раз, что обычно в заголовочный файл по
мещают директивы ^include', прототипы функций; определения
встроенных (inline) функций; объявления (extern) данных, опреде
ленных в другом файле; определения (const) констант; перечисления
(епит), директивы условной трансляции (#ifndef, i^endif VL др.), мак
роопределения (Udeflne), именованные пространства имен (name
space), определения типов (class, struct), объявления и определения
шаблонов (template).
Заголовочные файлы никогда не должны содержать определе
ния невстроенных функций, определения данных (объектов), опре
деления массивов и неименованные пространства имен.
2. Обратить внимание на оформление функций и, особенно, на
оформление заголовка функции.
3. Обратить внимание на оформление заголовочных коммента
риев файлов программного проекта.
Приложение П.б. Примерная программа дисциплины
"Программирование и основы алгоритмизации".
МИНИСТЕРСТВО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
УТВЕРЖДАЮ
Начальник Управления образовательных программ
и стандартов высшего и среднего профессиональ
ного образования
2000 г.
ПРИМЕРНАЯ ПРОГРАММА д и с ц и п л и н ы
434
1. Цели и задачи дисциплины
Цель дисциплины состоит в поэтапном формировании у студентов следую
щих слоев знаний и умений,
• Слой 1: знание основных понятий программирования.
• Слой 2: знание базового языка программирования.
• Слой 3: умение решать задачи на вычислительных машинах ВМ.
Формированию отмеченных уровней (слоев) знаний и умений соответствуют
разделы дисциплины. Изучение курса предполагает, что студенты знакомы с прин
ципами работы ВМ, десятичной, двоичной, восьмеричной и шестнадцатеричной
системами счисления, а также основными понятиями информатики.
4. Содержание дисциплины
4.L Разделы дисциплины и виды занятий
iN» п/п Раздел дисциплины Лекции ПЗ ЛР
(упражнения) (курс, рабо
та)
1 Основные понятия профаммрфования *•
2 Си/С+-1-: базовый язык программирования (алфавит, * *
синтаксис, типы данных, выражения и операции,
структурное программирование, операторы ветвлений
и циклов, массивы и структуры, модальное програм
мирование, функции, ввод и вывод, описатели класса
хранения, области действия и время жизни данных,
указатели, препроцессор)
3 Прикладное профаммирование (сортировки массивов, •
*
рекурсия и итерация, транспортная задача, элементы
обработки списков, работа с динамической памятью)
435
4,2. Содермсание разделов дисциплины
Раздел 1. Основные понятия программирования
(лекции и самостоятельная работа: 16 часов)
1. Алгоритм, данные, программа, структура данных.
2. Регистр: разрядность, графическое изображение. Совокупность регистров как ос
нова оперативного запоминающего устройства (ОЗУ). Адрес, хранимое слово.
Работа ОЗУ в режимах чтения и записи.
3. Простейший набор арифметических операций. Выполнение простейших арифме
тических операций.
4. Работа ВМ при последовательном выполнении команд. Ветвления в программах.
Команды условных переходов и безусловной передачи управления.
5. Прямая и косвенная адресация. Зачем нужна косвенная адресация?
6. Классификация и краткая характеристика языков программирования. Машинно-
зависимые язьпси: машинные (О GL - О Generation of the Language), ассемблеры (1
GL) и макроассемблеры (2 GL). Машинно-независимые языки: процедурные (3
GL), проблемные (4 GL) и универсальные (5 GL).
7. Введение в языки ассемблера (1 GL). Операторы языка ассемблера ВМ с "очень
простой" архитектурой. Символические команды. Псевдокоманды. Схема
трансляции в два прохода.
8. Периферийные устройства ВМ и их разновидности. Накопители на магнитных
дисках (НМД) с жёсткими дисками. Прямой и последовательный доступ к ин
формации. Монитор, работа в текстовом и графическом режимах. Клавиатура и
мышь. Принтеры. Взаимодействие программ с ПУ.
9. Программные продукты. Основные виды, этапы проектирования и жизненный
цикл.
436
процессе вычислений.
6. Оператор-выражение. Операции уменьшения и увеличения, префиксная и пост
фиксная форма. Операции простого и составного присваивания. Приоритеты опе
раций.
7. Оператор-выражение. Операции отношения. Результат вычисления отношений.
Представление булевских значений "ложь", "истина" в Си. Логические операции:
!, II, &&. Операции простого и составного присваивания. Приоритеты операций.
8. Структурное программирование. Операторы ветвления: if и switch. Оператор if,
синтаксис, выполнение оператора. Операции отношения. Составной оператор.
Сложные условия и логические операции: !, ||, &&. Вложение операторов if, опе
ратор if... else if... else. Оператор switch, выполнение, использование оператора
break.
9. Структурное программирование. Операторы цикла с предусловием (while) и по
стусловием (do-while). Примеры применения циклов. Изменение хода выполне
ния цикла с помощью операторов break и continue.
10. Одномерные массивы, пример использования. Связь массивов и указателей. Об
ращение к элементу массива, адресная арифметика. Структурное программиро
вание. Оператор цикла с шагом: for. Строки, кодовый формат, строковые литера
лы. Двумерные массивы. Функция индексации для двумерного массива.
11. Структуры, описание, пример использования. Операция выбора элемента струк
туры. Операции над структурой в целом. Структуры как аргументы функций.
Объявление типа: typedef. Размещение структур в памяти.
12. Модульное программирование. Функции. Рациональные размер и количество па
раметров функции. Пример функции. Аргументы и параметры. Передача аргу
ментов по значению и по ссылке. Прототипы функций. Преобразование аргумен
тов в точке вызова. Оператор return.
13. Ввод и вывод (передача) с преобразованиями. Понятие набора данных и файла.
Открытие и закрытие потоков. Функции передачи: fscanf и fprintf. Управляющая
строка, форматы при вводе и при выводе. Передача без преобразований (в кодо
вых форматах).
14. Время жизни и способ размещения данных. Спецификация класса памяти. Ста
тический способ размещения. Спецификаторы класса памяти extern и static. Ди
намический процесс исполнения программы и концепция памяти auto / register.
Данные с управляемым способом размещения, операции new и delete. Инициали
зация данных.
15. Объявления и определения. Область действия описаний. Структура программы
на языке Си. Переобъявления во вложенных блоках. Определения и объявления
на внешнем уровне. Определения и объявления на внутреннем уровне.
16. Указатели, адресная арифметика, указатели и массивы. Введение в обработку
списков. Создание и уничтожение узлов списка, вставка и исключение узлов спи
ска.
17. Препроцессор Си. Директива препроцессора #define. Терминология: макроопре
деление, макрообращение, макрорасширение. Макросы с параметрами, аналогия
с функциями. Директива препроцессора #include. Директивы условной компиля
ции.
По материалу разд. 2 в рамках практических занятий и самостоятельной рабо
ты выполняются следующие практические работы.
Проверочные работы по следующим темам (каждая длительностью 20-30 ми
нут):
- ввод-вывод с использованием функций fscanf - fprintf;
437
- ветвления и циклы;
- структуры;
- функции;
- области действия и время жизни объектов;
- указатели;
- операции с линейным списком.
Простой программный проект, предусматривающий решение трех элементар
ных задач с использованием языка C++ (однофайловые программы с одной функци
ей):
- программирование формулы:
- программирование ветвления;
- программирование цикла.
В процессе выполнения программного проекта изучаются программная документа
ция:
- единая система программной документации (ЕСПД);
- состав ЕСПД, назначение и содержание программных документов (пять из них -
"Техническое задание", "Текст программы", "Описание программы", "Программа и
методика испытаний" и схема программы - используются при оформлении про
граммного проекта).
Раздел 3, Прикладное программирование
(лекции, практические занятия и самостоятельная работа: 34 часа)
Цель этого раздела - овладение основами науки программирования, а также
профессиональным стилем программирования на Си/С++.
Выделено шесть часто встречающихся в приложениях типов задач: сортиров
ка массивов, рекурсивные методы, обработка списков, работа с таблицами, работа с
файлами, обработка текстов. Решение задач включает алгоритмизацию и програм
мирование. Умение решать эти задачи составляет основу профессиональной квали
фикации любого программиста.
Профессиональный стиль программирования подразумевает разработку про
стых и понятных исходных текстов программ. Важное внимание уделяется выбору
наиболее подходящих в каждом случае изобразительных средств языка и оформле
нию программ с учётом особенностей психики человека.
Рассматриваемые ниже задачи демонстрируют использование при проектиро
вании программного продукта иерархической декомпозиции задачи.
1. Сортировка: виды, терминология, обозначения. Простые алгоритмы сортировки
(выбором, вставками, обменом). Разработка функций, оценка производительно
сти.
2. Сортировка сложным выбором: с помощью двоичного дерева. Идея, пример ра
боты, разработка функции, оценка производительности.
3. Сортировка сложными вставками: метод Шелла. Идея, пример работы, разработ
ка функции, оценка производительности.
4. Сортировка сложным обменом: быстрая сортировка Хоора (нерекурсивный вари
ант). Идея, пример работы, разработка функции, оценка производительности.
5. Рекурсия и итерация. Рекурсия как метод вычислений. Рекурсивный вариант бы
строй сортировки Хоора. Когда не следует использовать рекурсию? Поиск пути
минимального суммарного веса во взвешенном неориентированном графе.
6. Элементы обработки списков. Инвертирование списка ссылок в задаче поиска
пути минимального суммарного веса во взвешенном неориентированном графе.
7. Сортировка массива сложным обменом: быстрая сортировка Хоора (рекурсив-
438
ный вариант).
По материалу разд. 3 в рамках практических занятий и самостоятельной работы
выполняется более сложный программный проект, реализующий решение некоторой
содержательной задачи. Проект содержит несколько файлов с исходным текстом и
несколько функций, расположенных в этих файлах. Большое внимание в проекте
уделяется методике тестирования спроектированного программного продукта.
Как указывалось выше, для более полной подготовки в области программиро
вания требуется формирование дополнительных знаний и умений, включая даль
нейшее развитие слоя 3 и освоение дополнительного слоя 4: умение проектировать
программное обеспечение. Достижению этой цели способствует последующий курс
"Технология программирования".
5. Лабораторный практикум (не предусматривается)
6. Учебно-методическое обеспечение дисциплины
6.7. Рекомендуемая литература
Основная литература:
1. Трои Д. Программирование на языке Си для персонального компьютера IBM PC:
Пер. с англ. - М.: Радио и связь, 1991.
2. Уэйт М., Прата С, Мартин Д. Язык Си: Пер. с англ. - М.: Мир, 1988.
3. Керниган Б., Ритчи Д., Фъюер А. Язык программирования Си: Задачи по языку
Си: Пер. с англ. - М.: Финансы и статистика, 1985.
4. Давыдов В.Г. Теория и технология программирования. Конспект лекций. Ч. 1. ~-
Санкт-Петербургский государственный технический университет, СПб.: 2000.
5. Давыдов В.Г. Теория и технология программирования. Конспект лекций. Ч. 2. -
Санкт-Петербургский государственный технический университет, СПб.: 2001.
Дополнительная литература:
1. Рассохин Д. От Си к Си++. М.: Издательство "ЭДЕЛЬ", 1993.
2. От Си к Си++ / Е.И. Козелл, Л.М. Романовская, Т.В. Русс и др. М.: Финансы и
статистика, 1993.
3. Эллис М., Строуструп Б. Справочное руководство по языку программирования
C++ с комментариями: Пер. с англ. - М.: Мир, 1992.
4. Бруно Б. Просто и ясно о C++: Пер. с англ. - М.: БИНОМ, 1995.
5. Пол Ирэ. Объектно-ориентированное программирование с использованием C++:
Пер. с англ. / Ирэ Пол. - К.: НИПФ "ДиаСофт Лтд", 1995.
6. Ю. Тихомиров. Visual C++ 6 - СПб.: БХВ - Санкт-Петербург, 1999.
7. Давыдов В.Г., Пекарев М.Ф. Учебный машинно-ориентированный язык (ПМ-
ассемблер): Учебное пособие. Санкт-Петербургский государственный техниче
ский университет, СПб.: 2000.
6.2, Средства обеспечения освоения дисциплины
Любая интегрированная среда программирования языка C++ (желательно бо
лее простая, чтобы сосредоточить внимание на вопросах программирования). Выби
рается по усмотрению вуза.
439
7. Материально-техническое обеспечение дисциплины
Компьютерный класс, оснащенный ПК не ниже Pentium 75 МГц с ОС
Windows 95 и выше или Windows NT.
8. Методические рекомендации по организации изучения
дисциплины
Использование специального методического обеспечения не предусмот
рено.
Программу составили:
443
3.9. Производные типы данных 81
3.9.1. Массивы 81
3.9.2. Массивы — как аргументы функций 86
3.9.3. Упраэюнения для самопроверки 88
3.9.4. Структуры 88
3.9.5. Структуры в качестве аргументов функций 91
3.9.6. Упраэюнения для самопроверки 93
4. ОПЕРАТОРЫ И УПРАВЛЕНИЕ ИХ ИСПОЛНЕНИЕМ .... 95
4.1. Пустой оператор 95
4.2. Операторы-выражения 95
4.3. Операторы break и continue 96
4.4. Блок операторов 96
4.5. Оператор return 97
4.6. Оператор if 97
4.7. Оператор switch 100
4.8. Оператор while 101
4.9. Оператор do-while 104
4.10. Оператор for 105
4.11. Оператор goto и метки операторов 108
4.12. Упражнения для самопроверки 108
5. ВЫРАЖЕНИЯ И ОПЕРАЦИИ 111
5.1. Операции ссылки 113
5.2. Унарные операции 116
5.3. Бинарные операции 118
5.4. Тернарная операция 121
5.5. Операция присваивания 121
5.6. Операция "запятая" 122
6. У К А З А Т Е Л И 123
6.1. Зачем нужны указатели? 123
6.2. Указатели и их связь с массивами и строками 123
6.3. Определение и применение указателей 124
6.4. Указатели на структуры 128
6.5. Использование указателей в качестве аргументов
функций 129
6.6. Указатель как значение, возвращаемое функцией 133
6.7. Массивы указателей 134
6.8. Замена типов указателей 137
6.9. Упражнения для самопроверки 141
7. ПОЛЯ БИТОВ И ПОБИТОВЫЕ ОПЕРАЦИИ 143
7.1. Поля битов 143
7.2. Побитовые операции 144
444
8. ДИНАМИЧЕСКОЕ РАЗМЕЩЕНИЕ ОБЪЕКТОВ В
ПАМЯТИ. ОДНОНАПРАВЛЕННЫЙ НЕКОЛЬЦЕВОЙ
ЛИНЕЙНЫЙ СПИСОК И ОПЕРАЦИИ С НИМ 148
8.1. Понятие об однонаправленном линейном списке.
Динамическое размещение объектов в памяти 148
8.2. Инициализация линейного списка 152
8.3. Добавление элемента в начало списка 163
8.4. Добавление элемента в конец списка 163
8.5. Создание ЛС с первым занесенным элементом
в начале 164
8.6. Создание ЛС с первым занесенным элементом
в конце списка 164
8.7. Удаление элемента из начала списка 165
8.8. Удаление элемента из конца списка 166
8.9. Разрушение ЛС с освобождением занятой им
динамической памяти 166
8.10. Печать содержимого ЛС 167
8.11. Добавление элемента после каждого элемента ЛС,
содержащего заданное значение 167
8.12. Добавление элемента перед каждым элементом ЛС,
содержащим заданное значение 168
8.13. Удаление элемента после каждого элемента ЛС,
содержащего заданное значение 168
8.14. Удаление элемента перед каждым элементом ЛС,
содержащим заданное значение 170
8.15. Зачем нужен линейный список 171
8.16. Упражнения для самопроверки 172
9. ПРЕПРОЦЕССОР ЯЗЫКА СИ/С++ 173
9.1. Директивы препроцессора 173
9.2. Подстановка имен 173
9.3. Включение файлов 177
9.4. Условная компиляция 178
9.5. Указания по работе с препроцессором 180
10. РЕДКО ИСПОЛЬЗУЕМЫЕ СРЕДСТВА
ЯЗЫКОВ СИ/С++ 182
10.1. Объявление имени типа typedef 182
10.2. Объекты перечислимого типа 183
10.3. Объединения 186
11. МОДЕЛИ ПАМЯТИ 189
11.1. Адресация near, far и huge 190
11.2. Стандартные модели памяти для
шестнадцатибитной среды DOS 193
445
11.3. Изменение размера указателей в
стандартных моделях памяти для
шестнадцатибитной среды DOS 194
11.4. Макроопределения для работы с указателями 195
11.5. Работа с памятью для среды WINDOWS 196
12. НОВЫЕ ВОЗМОЖНОСТИ ЯЗЫКА C++, НЕ
СВЯЗАННЫЕ С ОБЪЕКТНО-ОРИЕНТИРОВАННЫМ
ПРОГРАММИРОВАНИЕМ 197
12.1. Прототипы функций. Аргументы по умолчанию 198
12.2. Доступ к глобальным переменным, скрытым
локальными переменными с тем же именем 199
12.3. Модификаторы const и volatile 200
12.4. Ссылки 201
12.5. Подставляемые функции 202
12.6. Операции динамического распределения памяти 202
12.7. Перегрузка функций 203
12.8. Шаблоны функций 205
12.9. Перегрузка операций 206
13. ТЕХНОЛОГИЯ СОЗДАНИЯ ПРОГРАММ [5] 208
13.1. Кодирование и документирование программы 208
13.2. Проектирование и тестирование программы [5] 212
13.2.1. Этап 1: постановка задачи 213
13.2.2. Этап 2: разработка внутренних
структур данных 214
13.2.3. Этап 3: проектирование структуры
программы и взаимодействия модулей 214
13.2.4. Этап 4: структурное программирование 215
13.2.5. Этап 5: нисходящее тестирование 216
ЧАСТЬ 2. ПРИКЛАДНОЕ ПРОГРАММИРОВАНИЕ 218
14. ДИНАМИЧЕСКИЕ СТРУКТУРЫ ДАННЫХ [5] 218
14.1. Линейные списки 219
14.2. Бинарные деревья 220
14.3. Очереди и их частные разновидности 221
14.4. Реализация динамических структур с
помощью массивов 222
15. СОРТИРОВКА 224
15.1. Сортировка массивов 226
15.2. Сортировка массива простым выбором 228
15.3. Сортировка массива простыми включениями 248
15.4. Сортировка массива простым обменом
(метод "пузырька") 250
15.5. Выводы по простым методам сортировки 251
446
15.6. Сортировка массива сложным выбором
(с помощью двоичного дерева) 252
15.7. Сложная сортировка вставками (сортировка Шелла) ... 257
15.8. Сложная сортировка обменом (сортировка Хоора) 259
15.9. Сравнительные показатели производительности
различных методов сортировки массивов 264
16. ГРАФЫ. ТРАНСПОРТНАЯ ЗАДАЧА (ЗАДАЧА
КОММИВОЯЖЕРА) 266
16.1. Терминология 266
16.2. Формы задания графа 268
16.3. Почему для решения задачи подходит
рекурсивный алгоритм 269
16.4. Представление кратчайшего пути до каждой
вершины 270
16.5. Как найти минимальный путь 271
16.5.1. Требуется ли полный перебор путей 271
16.5.2. Организация перебора путей 271
16.6. Пример поиска минимального пути в графе 285
16.7. Печать информации о наилучшем пути 286
17. ПОИСК 288
17.1. Постановка задачи внутреннего поиска 288
17.2. Последовательный поиск 290
17.3. Логарифмический (бинарный) поиск 305
17.4. Поиск с использованием перемешанной
таблицы (хэш-таблицы) 308
18. ОТВЕТЫ И РЕШЕНИЯ К УПРАЖНЕНИЯМ
ДЛЯ САМОПРОВЕРКИ 312
18.1. Для подраздела 2.4.4 312
18.2. Для подраздела 3.8 314
18.3. Для подраздела 3.9.3 315
18.4. Для подраздела 3.9.6 317
18.5. Для подраздела 4.12 319
18.6. Для подраздела 6.9 321
18.7. Для подраздела 8.16 321
ПРИЛОЖЕНИЯ 327
Приложение П. 1. Тесты и программные проекты.
Варианты заданий 327
П. 1.1. Тесты (контрольные работы) 327
П. 1.1.1. Программирование на ПМ-ассемблере.
Варианты тестов 327
П.1.1.2. Ввод в языках Си/С++. Варианты тестов 330
П. 1.1.3. Вывод в языках Си/С++. Варианты тестов 337
447
п. 1.1.4. Простейшие ветвления. Варианты тестов 342
П.1.1.5. Циклы. Варианты тестов 345
П. 1.1.6. Структуры. Варианты тестов 350
П.1.1. 7. Функции. Варианты тестов 359
П. 1.1.8. Области действия определений. Варианты тестов 361
П. 1.1.9. Массивы и указатели. Варианты тестов 373
П. 1.1.10. Операции над линейным списком. Работа
с динамической памятью. Варианты тестов 379
П.1.1.11. Препроцессор, перечисления, функции с умалчиваемыми
значениями аргументов, перегрузка функций, шаблоны
функций, перегрузка операций. Варианты тестов 388
П.1.2. Программные проекты 392
П. 1.2.1. Программирование на ПМ-ассемблере. Варианты
программных проектов 393
П. 1.2.2. Структурное программирование средствами языков
Си/С-^+ . Варианты программных проектов 395
П. 1.2.3. Средства модульного программирования в языке C++.
Варианты программных проектов 402
П.1.3. Экзаменационное тестирование 405
Приложение П.2. Создание программного проекта 408
П.2.1. IDE MS Visual Studio C++ 6.0.
Создание программного проекта 408
П.2.2. IDE Borland C++ 3.1.
Создание программного проекта 411
Приложение П.З. Рекомендации по структуре
однофайловой программы с одной функцией и пример
оформления исходного текста 412
Приложение П.4. Методика отладки программы 414
П.4.1. Компиляция и компоновка программного
проекта. Устранение синтаксических огиибок 415
П.4.2. Отладка программного проекта. Устранение
логических (алгоритмических) огиибок 417
77.4.3. Тестирование программного проекта 420
Приложение П. 5. Рекомендации по созданию
многофайлового программного проекта с несколькими
функциями и пример оформления исходного текста 421
77.5.7. Спецификация программного проекта 421
П. 5.2. Пример оформления исходного текста
программы 428
Приложение П.6. Примерная программа дисциплины
"Программирование и основы алгоритмизации" 434
Приложение П.7. Прилагаемый компакт-диск 441
ЛИТЕРАТУРА 442
448