
- •1. Информация о дисциплине
- •1.1. Предисловие
- •Содержание дисциплины и виды учебной работы
- •2. Рабочие учебные материалы
- •2.2. Тематический план дисциплины
- •2.3 Структурно-логическая схема дисциплины
- •2.4 Временной график изучения дисциплины
- •2.5. Практический блок
- •2.6. Рейтинговая система по дисциплине «Программирование и основы алгоритмизации»
- •3. Информационные ресурсы дисциплины
- •3.1. Библиографический список
- •3.2. Опорный конспект лекций по дисциплине
- •Раздел 1. ОБЩИЕ ПОЛОЖЕНИЯ
- •Раздел 2. ПРОГРАММИРОВАНИЕ ТИПОВЫХ АЛГОРИТМОВ
- •Раздел 3. МАССИВЫ
- •Раздел 4.ГРАФИКА
- •3.3. Лабораторные работы
- •3.4. Практические занятия
- •Заключение
- •4.Блок контроля освоения дисциплины
- •4.1. Методические указания к выполнению курсового проекта
- •4.2. Блок тестов текущего контроля
- •4.3. Вопросы к экзамену
- •Содержание
Если же сделать панель невидимой (свойство Visible=true), то и все внутренние компоненты исчезнут с экрана.
Вопросы для самопроверки
1.В чем заключается понятие «тип данных»?
2.Объясните на примерах внутреннего («машинного») представления данных необходимость подпрограмм преобразования типов данных.
3.Какие действия производит машина по инструкциям-описателям типов данных, например int a; float b; AnsiString c?
4.Какие действия производит машина по оператору присваивания, например a=x;?
5.Для какой цели в языках программирования вводятся операторы цикла, если любой цикл можно организовать при помощи условного оператора if и оператора безусловного перехода goto?
6.В чем состоит сущность технологии визуального программирования?
7.Какой смысл заключается в утверждении, что любой компонент системы Builder является объектом?
8.Изобразите машинное представление данных основных типов– int, float и AnsiString.
9.В чём заключается смысл статического и динамического распределений памяти.
10.Как производится настройка свойств объекта, являющегося компонентом системы Builder?
11.Что такое «событийное» программирование? Как построить собственный обработчик события?
Раздел 2. ПРОГРАММИРОВАНИЕ ТИПОВЫХ АЛГОРИТМОВ
Изучаемые темы:
Тема 2.1.. Программирование прямых алгоритмов Тема 2.2.. Программирование разветвляющихся алгоритмов
Тема 2.3. Программирование циклических алгоритмов с известным количеством повторений Тема 2.4. Программирование алгоритмов с подпрограммами
Тема 2.5. Циклы с неизвестным количеством повторений Лабораторные работы:
Работа 1. Обработка скалярных данных Работа 2. Логические выражения
Работа 3. Циклы с известным количеством повторений Работа 4. Итерационные циклы
Тесты:
Тест 3. Прямые алгоритмы Тест 4. Обработка строк
Тест 5. Разветвляющиеся алгоритмы Тест 6. Циклические алгоритм

Тест 7. Алгоритмы с процедурами Тест 8. Локализация переменных
Максимальное число баллов: 36
Тема 2.1. Программирование прямых алгоритмов
Структура прямого алгоритма
Прямые алгоритмы представляют собою основу вычислительных процессов. Это такие алгоритмы, каждый блок которых выполняется всегда, причём только один раз.
Впрямом алгоритме надо предусмотреть следующие действия:
•установку значений аргументов вычислений;
•собственно вычисления;
•представление результатов.
Рассмотрим этапы создания проекта, реализующего прямые алгоритмы на примере следующей задачи : требуется построить имитатор арифметического калькулятора, точнее говоря программу, выполняющую четыре основных действия арифметики.
Построение программы начинается с проектирования интерфейса. Интерфейс для решения таких задач включает надписи, окошки для ввода и представления данных, кнопки для инициализации вычислений.
Вашему вниманию предлагается интерфейс, представленный на рис.17.
Рис. 17. Арифметический калькулятор для чисел типа float
Смысл работы программы понятен из объяснений на рисунке.
Программирование
Выберем следующие программные имена (свойство Name) тех компонентов, к которым будем обращаться из программы (табл. 21).

Таблица 21. Программные имена компонентов арифметического калькулятора
Компонент |
Класс |
Name |
Экранная форма |
TForm |
frmCalculator |
Кнопка сложения |
TButton |
btbPlus |
Кнопка вычитания |
TButton |
btbMinus |
Кнопка умножения |
TButton |
btbUmn |
Кнопка деления |
TButton |
btbDel |
Кнопка выхода |
TButton |
btbExit |
Окно первого операнда |
TEdit |
Edit1 |
Окно второго операнда |
TEdit |
Edit2 |
Окно результата |
TEdit |
EditRes |
После составления интерфейса начинается собственно процесс программирования, который заключается в установлении связей между событиями и соответствующими процедурами. Чтобы обоснованно выбрать событие, программист должен детально ознакомиться со списком допустимых для каждого компонента событий и выбрать подходящее по контексту задачи. Трудность состоит в том, что не всегда ясно, в какой последовательности наступают события, ведь не все они инициируются щелчком мыши. Этот вопрос будет рассмотрен в конце пособия, а сейчас мы будем ориентироваться только на события, вызванные пользователем.
В приведенной задаче достаточно использовать одно событие - щелчок левой клавишей по компонентудля реализации всех действий. Таким образом, общий процесс программирования будет таким:
1.Установите курсор на компонент "кнопка" ( типа TButton)
2.Дважды щелкните левой клавишей по компоненту, чтобы Builder построил заготовку обработчика
3. В открывшемся окне программного кода запишите программу, соответствующую назначению кнопки 4. Сохраните программный модуль в выбранном индивидуальном каталоге
Вычислительный алгоритм логически очень прост:
1. Установка начальных значений аргументов вычисления (переменные a и b).
2.Вычисления (переменная c).
3.Вывод результата.
Ввод исходных данных в C++ можно сделать самыми разнообразными способами:
1. Задать начальные значения в операторах описания переменных, например:
int a=5; float b=3.57;
2.Установить начальные значения оператором присваивания: int a; float b;
a=5; b=3.57;
3.Ввести значения с пульта при выполнении программы, как в нашем примере.
4.Ввести значения с диска из файла или из базы данных.
Очевидно, что каждый из способов адекватен конкретной задаче. Поскольку в нашем примере требуется выполнять счёт над различными числами, их надо вводить с пульта при выполнении задачи. Первый и второй варианты ввода удобны для демонстрации разовых выполнений алгоритма.
Способы вывода результатов также весьма разнообразны:
1.Можно выводить значения в любой компонент, обладающий свойствами Caption (надпись) или Text (текст) . Например, можно использовать компонент Label (метка):
Label1->Caption=IntToStr(c);
2.Наиболее часто используют компонент Edit, дающий на экране хорошо видимое окно:
Edit1->Text=IntToStr(c);
Ниже приведен полный листинг одного из вариантов программы, показывающей выполнение арифметических операций над вещественными
числами (типа float). |
|
//Арифметические операции |
|
#include <vcl.h>// 1 |
инструкции 1...5 являются командами, |
|
//управляющими работой компилятора. |
|
//Их называют директивами компилятора, |
|
//а строит их Builder автоматически. |
#pragma hdrstop |
//2 |
#include "uCalculator.h" |
//3 |
#pragma package(smart_init) |
//4 |
#pragma resource "*.dfm" |
//5 |
TfrmCalculator *frmCalculator; // Эта инструкция также построена Builder-ом
__fastcall TfrmCalculator::TfrmCalculator(TComponent* Owner)
:TForm(Owner)
{//Эта процедура называется конструктором формы Внешне это пустая //подпрограмма, но это не так: многострочный программный код, //полностью построенный Builder-ом, просто не афишируется, так
//как никаких изменений в нем делать не надо.
//Никогда не удаляйте этот блок и ничего сюда не записывайте !
}
//--------------------------------------------------------------
//Блок завершения программы
void __fastcall TfrmCalculator::btbExitClick(TObject *Sender) {Close(); }//--------------------------------------------------------------
//Сложение
void __fastcall TfrmCalculator::btbPlusClick(TObject *Sender) {float a,b,c;
AnsiString s;
a=StrToFloat(Edit1->Text);//преобразование строки в число b=StrToFloat(Edit2->Text);
c=a+b;
s=FloatToStr(c);//преобразование числа в строку
EditRes->Text=s;//вывод результата
}//конец процедуры сложения //Вычитание
void __fastcall TfrmCalculator::btbMinusClick(TObject *Sender) {float a,b,c;
AnsiString s; a=StrToFloat(Edit1->Text); b=StrToFloat(Edit2->Text); c=a-b;
s=FloatToStr(c); EditRes->Text=s;
}//--------------------------------------------------------------
//Умножение //Эта процедура для примера записана более коротким, но менее понятным
//способом. Если такой текст понятен, можно им пользоваться, но в //начальной стадии изучения программирования лучше всё же обращаться к //помощи вспомогательных переменных, как в блоках сложения и вычитания void __fastcall TfrmCalculator::btbUmnClick(TObject *Sender)
{EditRes->Text= FloatToStr(StrToFloat(Edit1->Text)*StrToFloat(Edit2->Text)); }//--------------------------------------------------------------
//Деление
void __fastcall TfrmCalculator::btbDelClick(TObject *Sender) {float a,b,c; AnsiString s;
a=StrToFloat(Edit1->Text); b=StrToFloat(Edit2->Text); c=a/b;
s=FloatToStr(c); EditRes->Text=s;
}
Листинг 1. Программный модуль арифметического калькулятора для чисел float
Программистам приходится очень редко вручную корректировать текст заголовочного модуля, только тогда, когда определяются глобальные переменные и собственные классы объектов.
Функции преобразования типов
В операторах присваивания типы переменных слева и справа от символа присваивания должны совпадать. В простейших случаях за этим следит компилятор и обеспечивает автоматическое приведение типов. При использовании полей и свойств компонентов следить за совпадением типов должен программист.
Все визуальные компоненты, которые изображают данные на экране, хранят значения данных в своих свойствах (полях) типа AnsiString. Прежде всего это свойство Text компонента TEdit, свойство Items компонентов
TListBox и TComboBox, свойство Lines для TMemo и т.д. Поэтому при обмене данными между такими компонентам и числовыми переменными необходимо прибегать к встроенным функциям преобразования типов
(табл. 22).
Таблица 22. Функции преобразования типов
Пример записи функции |
Тип |
Преобразование |
Результат |
|
аргумента |
|
|
StrToInt("123") |
AnsiString |
строка в целое число |
123 |
StrToFloat("123.456") |
AnsiString |
строка в вещественное число |
123.456 |
|
|
|
|
IntToStr(123) |
int |
целое в строку |
"123" |
FloatToStr(1.23678E2) |
float |
вещественное в строку. |
"123.67800032" |
|
|
Десятичные числа, абсолютная |
|
|
|
величина которых не |
|
|
|
превосходит 10 в 15-й степени, |
|
|
|
преобразуются в формат с |
|
|
|
фиксированной точкой с 7-8 |
|
|
|
знаками после точки. |
|
|
|
Младшие разряды, которые в |
|
|
|
быту всегда округляются, могут |
|
|
|
появится на экране - в примере |
|
|
|
00032 |
|
FloatToStr(1.23678E17) |
float |
вещественное в строку |
1.23678E17 |
|
|
Поскольку значение числа |
|
|
|
велико, оно и на печати остается |
|
|
|
в экспоненциальной форме |
|
Таблица 22. Функции преобразования типов (продолжение)
Пример записи |
Тип |
Преобразование |
Результат |
функции |
аргумента |
|
|
FloatToStrF(1.23687E2, |
float |
вещественное в строку с |
123.69 |
ffFixed,8,2) |
|
фиксированным положением точки |
|
|
|
Здесь ffFixed - системная константа, |
|
|
|
указывающая тип формата. В данном |
|
|
|
случае для экранного изображения |
|
|
|
числа отводится 8 символов, два из |
|
|
|
которых идут после разделительной |
|
|
|
точки. Число округляется по |
|
|
|
арифметическим правилам до |
|
|
|
младшего десятичного разряда |
|
В листинге 1 программы повсюду использованы простые преобразователи плавающего числа FloatToStr. При делении обязательно возникнут длинные числа с неопределёнными значениями в младших разрядах. Для большей эстетики, конечно, надо было бы использовать функцию преобразования в фиксированный формат FloatToStrF, но в целях упрощения программного текста здесь и далее будут употребляться функции
FloatToStr.
Проблема разделительной точки |
|
Все языки программирования предполагают, что |
в качестве |
разделительного знака используется точка, то есть число имеет вид 123.456. Если же в программе записать число с запятой 123,456 , то компилятор определит такую запись как ошибочную.
Однако в русифицированных версиях Windows почему-то по умолчанию в качестве разделителя установлена запятая. Возникает противоречие между программным изображением числа и его видом в окне ввода на экране. Например, если в окно Edit1 программы ArCalc (рис. 17) записать число 123.456 с разделительной точкой, а операционная система установлена с параметрами по умолчанию, то при попытке выполнить любую арифметическую операцию возникнет аварийный останов с таким сообщением на экране :
123.456 is not a valid float value
(123.456 не является допустимым плавающим значением)
Естественно это приводит начинающего программиста в недоумение, поскольку число-то записано правильно, а то , что разделителем в данном окружении служит запятая, на видном месте не написано.
Обработка строк
Операции над строковыми значениями производятся при помощи встроенных в язык функций, их список полностью представлен в справочной системе Builder, где его можно найти по запросу
String routines. Quick reference
(Строковые подпрограммы. Быстрая справка).
К сожалению, текст справки изложен на английском языке. На русском полный перечень этих подпрограмм можно найти в [1,2 и 3]. Здесь же будет описано небольшое подмножество функций и процедур, используемых в данном пособии.
Встроенные функции обработки строк типа AnsiString
Рассмотрим наиболее распространенные функции обработки строк с пояснением их действий на примерах, в которых использованы следующие переменные :
AnsiString str1,str2, str3; |
int pos,kol,len; |
При объяснении действий функции часто используют выражение типа "функция возвращает значение". Обычно вызов функции записывается не как самостоятельная процедура (хотя возможны и такие варианты), а как член некоторых выражений. При этом функция по своему внутреннему алгоритму определяет числовое или строковое значение, которое подставляется в исходное выражение, замещая собой вызов функции.
1. Length Длина сроки
Пример: str1="ABCDE"; x= s1.Length();
Результат: x=5;
Выполнение: функция Length считывает нулевой байт поля, в котором записана строка. В этом байте записано целое число, равное длине строки в символах (см. раздел «Машинное представление данных»). Таким образом, значение функции равно длине строки. Аргумент в круглых скобках не указывается.
Выражение "функция возвращает значение" следует понимать, что подпрограмма подставляет своё значение в точку вызова, поэтому после выполнения функции переменная x приобретает значение 5.
2. Соединение строк (конкатенация)
Символом функции служит обыкновенный плюс (+).
Пример: str3=str1+str2;
Результат: при str1="ABC" и str2="123" значение str3=" ABC123".
Выполнение: Байты строки str2 добавляются к байтам строки str1. Результирующая строка имеет длину, равную сумме длин исходных строк
3. Сравнение строк на предмет равенства
Пример: str1==str2
Результат: true, если все байты str1 равны байтам str2; false, если имеется хотя бы одно несовпадение.
Выполнение: сравниваются коды символов последовательно слева направо. Если строки имеют неравную длину, то более короткая строка удлиняется пробелами до более длинной. Как только встречается первая пара символов, не отвечающих поставленному условию, значению логического выражения присваивается false и сравнение прекращается. Если коды всех пар символов совпадают, условию сравнения присваивается true.
Для понимания результатов сравнения надо прибегнуть к кодировочной таблице символов (табл. 5).
Другие варианты сравнения строк Допустимы все виды сравнения, а именно:
str1>str2 |
больше |
str1>=str2 |
больше или равно |
str1<str2 |
меньше |
str1<=str2 |
меньше или равно |
str1!=str2 |
не равно |
Пример: пусть str1='ABCD' str2='ABC'
тогда выражения str1>str2 равно true str1==str2 равно false str1!=str2 равно true str1<str2 равно false
4. AnsiPos - Позиция подстроки в строке
Пример: pos=AnsiPos(str2,str1);
Результат: при str1=" ABCDE " и str2="CD" pos=3
при str1=" ABCDE " и str2="CE" |
pos=0 |
при str1=" ABCDE " и str2="D" |
pos=4 |
при str1=" ABCDE " и str2="d" |
pos=0 |
Выполнение: функция AnsiPos определяет, является ли подстрока str2 подмножеством строки str1. Если да, то возвращаемое значение равно порядковому номеру того элемента строки str1, где произошло первое совпадение. Если есть и другие фрагменты строки, где тоже могут быть совпадения, они не определяются. Если факт вхождения подстроки str2 не
определен, то возвращаемое значение равно нулю. Строчные и прописные буквы считаются различными.
5. SubString - Копирование подстроки из строки
Пример: str2=s1.SubString(pos,kol); Результат: если str1=" ABCDE ", то при pos=3, kol=2 str2= "CD"
при pos=1, kol= 1 str2= "A"
при pos=6, kol=2 str2= "" (пустая строка)
Выполнение: начиная с позиции <pos> строки str1, функция SubString выделяет <kol> символов и возвращает их в качестве найденного значения.
6. Delete - Удаление подстроки из строки
Пример: str2=s1.Delete (pos,kol); Результат: если str1=" ABCDE ", то при pos=3, kol=2 str2= "ABDE"
при pos=1, kol= 1 str2= "BDE" при pos=6, kol=2 str2= "ABCDE"
Выполнение: начиная с позиции <pos> строки str1, функция Delete удаляет <kol> символов и возвращает оставшиеся символы как значения функции.
7. Inset - Вставка подстроки в строку
Пример: |
|
s1.Inset (s2,pos); |
Результат: |
|
если str1=" ABCDE "и str2="123", то |
при pos=3 |
str1= "AB123СDE" |
|
при pos=1 |
str2= "123ABCDE" |
|
при pos=6 |
str2= "ABCDE123" |
|
Выполнение: |
данная функция используется как процедура, то есть как |
самостоятельный оператор, а не как член выражения. В исходную строку str1 вставляется подстрока str2 c позиции <pos> . В результате строка str1 удлиняется на количество символов строки str2.
8. Приведение строки к верхнему регистру
Пример: str2=AnsiUpperCase(str1);
Результат: если str1=" AbcDe ", то s2= " ABCDE "
Выполнение: все маленькие буквы исходной строки заменяются большими. Если символ не буква, никаких преобразований над символом не производится. Число символов в результирующей строке не изменяется.
Примечание. Функция не полностью применима к русским буквам, так как для кириллического алфавита не выдержан лексикографический порядок кодировки (табл. 7)
9. Приведение строки к нижнему регистру
Пример: str2=AnsiUpperCase(str1); Результат: если str1=" AbcDe ", то s2= " abcde "
Выполнение: все большие буквы исходной строки заменяются маленькими. Если символ не буква, никаких преобразований над символом не производится. Число символов в результирующей строке не изменяется Примечание. Функция не полностью применима к русским буквам, так как для кириллического алфавита не выдержан лексикографический порядок кодировки (табл. 7).
10. Удаление начальных и концевых пробелов
Пример: |
str2=Trim(str1); |
|
Результат: если str1=" AbcDe |
", то s2= "AbcDe " |
|
Имеется три сходных функции: |
|
|
Trim |
возвращает строку S с удаленными начальными и конечными |
пробельными и управляющими символами.
TrimLeft возвращает строку S с удаленными начальными пробельными и управляющими символами.
TrimRight возвращает строку S с удаленными конечными пробельными и управляющими символами.
Использование функции обработки строк
При вводе информации о работниках предприятия в базу данных всегда возникает вопрос: каким образом минимизировать ошибки набора символов на пульте? Типичными ошибками являются :
•ввод латинских символов вместо русских;
•ввод ненужных пробелов;
•набор прописных букв вместо строчных и наоборот.
Если программа ввода не обеспечивает защиту от появления недопустимых символов, это может привести к недоразумениям, которые не очень просто исправить. Эти ошибки наиболее ярко проявляются при сортировке строковых массивов. Предположим, напечатан такой список:
Петров
Сидоров
Тарасов
борисов
Авдеев
Ёлкин
После сортировки по алфавиту он неожиданно принимает вид: Тарасов Сидоров Ёлкин Авдеев Петров борисов
Но такой порядок полностью противоречит ожидаемой последовательности. Почему все фамилии переместились не на свои места ? Вот возможные причины.
При сортировке строк по алфавиту происходит попарно сравнение строк и "меньшая" строка ставится выше "большей".
Встроке "Тарасов" первый символ - пробел с кодом 32 (табл. 4). Это самый младший код символов, поэтому эта строка занимает первое (младшее место).
Встроке " Сидоров " первый символ не русская буква C(код 209), а английская С (код 67). Оператор не заметил, что на клавиатуре был установлен английский язык, а изображения русской и английской букв совпадают.
Встроке "Ёлкин" буква Ё имеет код 168, тогда как буква А закодирована как 192, поэтому Ёлкин идёт в списке раньше Авдеева. Это следствие несоблюдения лексикографического порядка в таблице кодов
ANSI.
Фамилия "борисов" напечатана с маленькой буквы. Все коды маленьких букв старше больших, поэтому Борисов попал в конец списка
Чтобы избавиться от такого перемешивания, программа должна исправлять возможные ошибки ввода или вообще не допускать ввод неразрешенных по контексту символов. Построение программы, не допускающей ввод неразрешённых символов, рассматривается в курсе «Прикладное программирование». Здесь же рассмотрим пример, где показано, как избавиться от ненужных пробелов и правильно записывать большие и маленькие буквы.
Пусть с пульта вводятся фамилия, имя и отчество работника, причем допускается вводить без разбора как прописные, так и строчные буквы. После преобразования строка должна отвечать следующим требованиям:
•первая буква – прописная;
•все остальные буквы – строчные;
•в строке отсутствуют как начальные, так и конечные пробелы.
Будем называть такое преобразование "нормализацией" строки.
Возможный интерфейс представлен на рис. 18, а обработчика с детальными комментариями, совершающего преобразования строк, в листинге 2. Общая идея алгоритма нормализации следующая:
• убрать из строки концевые пробелы

•всю строку привести к нижнему регистру
•первую букву привести к верхнему регистру
Рис. 18. Интерфейс программы нормализации имён собственных
void __fastcall Tform1: btbNormaClick(TObject *Sender) {AnsiString s1,s2,s3,srab;//srabрабочая переменная s1=Edit1->Text;//фамилия
s2=Edit2->Text;//имя s3=Edit3->Text;//отчество
s1=Trim(s1); s2=Trim(s2); s3=Trim(s3);//удаляем пробелы из всех строк
//1. Преобразование первой строки s1=AnsiLowerCase(s1);//приводим строку к нижнему регистру sr=s1.SubString(1,1);// здесь все буквымаленькие sr=AnsiUpperCase(sr);
srab=s1.SubString(1,1);//srab - первая буква фамилии srab=AnsiUpperCase(srab);//Преобразовали первую букву в прописную s1.Delete(1,1);//удалили первую букву
Edit4->Text= srab+s1;//соединили первую прописную букву с остальными // строчными
//2. Преобразование второй строки производится аналогично s2=AnsiLowerCase(s3);
sr=s2.SubString(1,1);//
sr=AnsiUpperCase(sr);
srab=s2.SubString(1,1);
srab=AnsiUpperCase(srab);
s2.Delete(1,1); Edit5->Text= srab+s2;
//3. Преобразование третьей строки производится аналогично s3=AnsiLowerCase(s3);
sr=s2.SubString(1,1);//
sr=AnsiUpperCase(sr);
srab=s3.SubString(1,1);
srab=AnsiUpperCase(srab);
s3.Delete(1,1); Edit6->Text= srab+s2;
s2=s2.SubString(1,1); s2=AnsiUpperCase(s2)+"."; s3=s3.SubString(1,1); s3=AnsiUpperCase(s3)+"."; Edit4->Text=s1+' '+s2+s3;
}
Листинг 2. Простейшая программа нормализации строк
В программе предполагается, что кнопка “Нормализация” имеет программное имя “btbNorma”.
Подпрограммы
Вданном примере надо было нормализовать три различных строки – Фамилию, Имя и Отчество, применяя к ним один и тот же алгоритм. Такая программа естественным образом требует применения механизма подпрограмм.
Влистинге 3 приведен пример решения той же самой задачи, но с
использованием собственной процедуры StrNorma. Студент должен самостоятельно разобраться в приведенном тексте, что входит в состав самостоятельно выполняемых работ.
//-----собственная функция нормализации строки s -----------------------------
AnsiString StrNorma( AnsiString s)//StrNormaимя функции {AnsiString sr;
s=Trim(s);
s=AnsiLowerCase(s);
sr=s.SubString(1,1);
sr=AnsiUpperCase(sr);
s.Delete(1,1);
s=sr+s;
return s; // специальный оператор, передающий результат s в точку вызова }//конец функции нормализации строки
//основная программа
void __fastcall TForm1:: btbNormaClick (TObject *Sender)
{
AnsiString s1,s2,s3;
s1=StrNorma(Edit1->Text); //вызов функции нормализации для
//строки "фамилия" s2=StrNorma(Edit2->Text);//вызов функции нормализации для строки "имя" s3=StrNorma(Edit3->Text);//вызов функции нормализации для строки "отчество"
Edit4->Text=s1;
Edit5->Text=s2;
Edit6->Text=s3;
}//конец обработчика btbNormaClick
Листинг 3. Программа нормализации строк с использованием процедуры
Из данного примера видно, что программирование с использованием процедур, гораздо эффективнее простого программирования по следующим причинам:
1.не надо повторно переписывание похожие участки алгоритма,
2.текст программы становится короче и понятнее,
3.когда процедура отлажена и в ней нет ошибок, при её повторном использовании не надо вновь заниматься отладкой. Следовательно, если ошибки появились, их следует искать вне процедур. Значит, отладка программ упрощается,. а надежность программирования увеличивается,
4.когда личные процедуры собраны в библиотеку, её можно подключить к другому программному проекту, и тогда разработка следующего проекта будет значительно проще и быстрее. Значит, процедуры способствуют повышению производительности труда программиста.
Вопросы для самопроверки
1.Какие операторы C++ являются минимально необходимыми для построения прямого (линейного) алгоритма?
2.Приведите минимальный набор компонентов Builder-а, необходимый для построения прямого алгоритма.
3.Какими способами в программе устанавливаются начальные значения аргументов?
4.Почему иногда при сложении двух положительных целых чисел результат становится отрицательным?
5.Какие компоненты удобно использовать для ввода данных на этапе выполнения программы (на фазе run time)?
6.Какие компоненты используются для вывода данных на экран?
7.В чем состоит особенность представления на экране данных типа float? Как изобразить такое число в формате с фиксированной точкой?
8.Изложите правила выполнения сложных арифметических выражений с точки зрения приоритета операций.
Тема 2.2. Программирование разветвляющихся алгоритмов
На практике редко встречаются задачи, алгоритм решения которых является прямым или линейным. Например, пусть надо вычислить по формуле ток в электрической цепи. Если предположить, что пользователь всегда будет вводить верные данные, то алгоритм решения этой задачи действительно является линейным. Однако полагаться на то, что пользователь будет вести себя так, как надо программе, не следует. Формула расчета предполагает, что величина сопротивления не равна нулю. А что будет, если пользователь введет 0? Ответ простой: возникнет ошибка "Деление на ноль", и программа аварийно завершит работу. Можно, конечно, возложить ответственность за это на пользователя, но такая программа будет ненадежной и не будет пользоваться рыночным спросом. Лучше внести изменения в алгоритм решения, чтобы расчет выполнялся только в том случае, если введены верные данные.
Точки алгоритма, в которых выполняется выбор дальнейшего хода программы, называются точками выбора или точками принятия решений. Выбор очередного шага решения задачи осуществляется в зависимости от выполнения некоторого условия.
Условие
В повседневной жизни условие обычно формулируется в виде вопроса, на который можно ответить Да или Нет. Например:
Величина сопротивления равна нулю? Ответ правильный?
В программе условие — это выражение логического типа (bool), которое может принимать одно из двух значений: true (истина) или false (ложь).
Простое условие состоит из двух операндов и оператора сравнения. В общем виде условие записывается следующим образом:
<Операнд 1> <Операция сравнения> <Операнд 2>, где Операнд 1 и Операнд 2 — операнды условия, в качестве которых может
выступать переменная, константа, функция или выражение.
В языке С++ есть шесть операторов сравнения, которые приведены в табл 23.
|
|
Таблица 23. Операции сравнения |
Операция |
Описание |
Результат сравнения |
> |
Больше |
true, если первый |
|
|
операнд больше |
|
|
второго, иначе true |
< |
Меньше |
true, если первый |
|
|
операнд меньше |
|
|
второго, иначе true |
= = |
Равно |
true, если первый |
|
|
операнд равен второму, |
|
|
иначе |
|
|
true |
!= |
Не равно |
true, если первый |
|
|
операнд не равен |
|
|
второму, иначе true |
>= |
Больше или равно |
true, если первый |
|
|
операнд больше или |
|
|
равен второму, иначе |
|
|
true |
<= |
Меньше или равно |
true, если первый |
|
|
операнд меньше или |
|
|
равен второму, иначе |
|
|
false |
Примечание. Обратите внимание, что в качестве операции сравнения на предмет равенства используется сдвоенный знак «равно»- = =. Если в этом контексте использовать одиночный знак = («равно»), то произойдет не сравнение операндов, а присваивание и в целом инструкция сравнивания не будет работать.
Ниже приведены примеры условий: sum < 123
X >= Y
В первом примере операндами условия является переменная и константа. Значение этого условия зависит от значения переменной sum. Условие будет верным и, следовательно, иметь значение true, если значение переменной sum меньше, чем 123. Если значение переменной sum больше или равно 123, то значение этого условия будет false.
Во втором примере в качестве операндов используются переменные. Значение этого условия будет true, если значение переменной X больше или равно значению переменной Y.
При записи условий следует обратить особое внимание на то, что операнды условия должны быть одного типа или, если тип операндов разный, то тип одного из операндов может быть приведен к типу другого операнда. Во время трансляции программы при несовпадения типов операндов компилятор выводит сообщение: incompatible types (несовместимые типы).
Из простых условий при помощи логических операций : && ("логическое И"), || -- "логическое ИЛИ" и ! - "отрицание" можно строить
сложные условия.
В общем виде сложное условие записывается следующим образом: <условие1 ><логическая операция>< условие2>, где условие1 и условие2 — простые условия (выражения логического типа); логическая операция —«&&» , «||» , «!».
Например, если переменная ch имеет тип char, то условие
(ch >= '0') and (ch <= '9')
определяет принадлежность символа ch к подмножеству «цифры», то есть к последовательности 0, 1, 2… 9
Результат выполнения логических операций «&&» , «||» , «!» над булевскими операндами Op1 и Op2 представлен в табл. 24.
|
|
Таблица 24. Выполнение логических операций |
||
Операнд 1 |
Операнд 2 |
Op1 && |
Op1 || Op2 |
! Op1 |
(Op1) |
(Op2) |
Op2 |
|
|
false |
false |
false |
false |
true |
false |
true |
false |
true |
true |
true |
false |
false |
true |
false |
true |
true |
true |
true |
false |
При записи сложных условий важно учитывать то, что логические операторы имеют более высокий приоритет, чем операторы сравнения, и поэтому простые условия следует заключать в скобки.
Например, пусть условие предоставления скидки сформулировано следующим образом: "Скидка предоставляется, если сумма покупки превышает 100 руб. и день покупки — воскресенье". Если день недели обозначен как переменная Day целого типа, и её значение семь соответствует воскресенью, то условие предоставления скидки можно записать:
(Summa > 100) && (Day = 7).
Условный оператор if
Инструкция if позволяет выбрать один из двух возможных вариантов развития программы. Выбор осуществляется в зависимости от выполнения условия.
В общем виде инструкция if записывается так:
if (<условие>)
{// инструкции группы 1, которые надо выполнить, // если условие истинно.
}; else
{// инструкции группы 2, которые надо выполнить, // если условие ложно.
}; // следующий оператор.
Обратите внимание, что перед else (после закрывающей фигурной скобки }) ставится точка с запятой, но в этом контексте она не является разделителем операторов.
Выполняется инструкция if следующим образом:
1.Вычисляется значение условия (условие — выражение логического типа, значение которого может быть равно true или false).
2.Если условие истинно (значение выражения условие равно true), то выполняются инструкции группы1, группа 2 пропускается, после чего выполняется следующий по тексту оператор.
Если условие ложно (значение выражения условие равно false), то группа 1 пропускается, выполняются инструкции группы 2, после чего выполняется следующий по тексту оператор.
Например, если переменная type обозначает тип соединения сопротивлений в электрической цепи (type =1 соответствует последовательному соединению, type =2 — параллельному), a r1 и r2 — величины сопротивлений, то приведенная ниже инструкция if осуществляет выбор формулы, по которой будет выполнен расчет общего сопротивления цепи.
if (type = =1) {z=r1+r2;}
else {z=(r1+r2)/(r1*r2);}
Если группа инструкций состоит только из одной команды, , то фигурные скобки могут быть опущены. Тогда предыдущий оператор можно записать так:
if (type = =1)
z=r1+r2;
else z=(r1+r2)/(r1*r2);
или вообще в одну строку
if (type = =1) z=r1+r2; else z=(r1+r2)/(r1*r2);
Сокращенная форма условного оператора
В ряде случаев бывает удобной сокращенная форма условного оператора, у которого отсутствует else – часть:
if (<условие>)
{ //инструкции группы 1
}
Выполняется такой оператор if следующим образом:
Вычисляется значение условия и если оно истинно (true), то выполняется группа инструкций 1, после чего выполняется следующий по тексту оператор. Если условие ложно (false), группа инструкций 1 пропускается и происходит переход к следующему оператору.
Особенности программирования сложных условных выражений
Предположим, надо запрограммировать такое условие:
«Если сегодня – суббота, то Алла поедет кататься на лыжах, если – воскресенье, то она пойдет в кино, если понедельник, вторник, среда или четверг, то в школу, а в пятницу она пойдет в поликлинику».
Чтобы запрограммировать выбор одного варианта из многих, можно воспользоваться условным оператором if, однако он удобен, только если надо сделать выбор из двух или максимум трех возможных значений. При большем количестве вариантов используют специально предназначенный для этой цели оператор switch (переключатель), с которым студенты будут знакомиться в рамках лабораторных работ.
Следует обратить внимание на понятие блока - последовательность операторов, заключённых в фигурные скобки. Блок используется для того, чтобы группа операторов воспринималась как один целый оператор. В этом смысле фигурные скобки блока играют такую же роль, как и обычные круглые скобки в алгебре. Однако только этим роль блока не ограничивается:
блок показывает границы использования переменных, описанных в этом блоке, то есть он указывает на область существования переменной, определяя её локализацию.
Локализация переменных
Каждая переменная должна быть описана с помощью указателя её типа. Описание может быть сделано в любой точки блока, но хороший стиль структурного программирования требует, чтобы все переменные были бы описаны в начале блока:
{//начало блока
int a,b; float x; bool c; // место описания переменных // операторы блока
}// конец блока
Следует запомнить правило: описание локализует переменную в блоке. Локализация означает, что смысл и значение переменной определены только в том блоке, где она описана.
Понятие |
локализации является одним из фундаментальных |
достижений |
технологии программирования, поскольку он позволяет |
разрабатывать большие программные системы по модульному принципу и обеспечивает надежность программирования.
Суть в том, что большие программы, скажем, содержащие сотни тысяч строк программного текста, не могут быть составлены одним человеком. Программу надо разбить на модули и поручить разработку каждого модуля отдельным программистам. Каждый модуль автоматически становится блоком. Если бы не существовало локализации переменных, тогда в разных модулях имена всех переменных должны были бы отличаться, что определенно невозможно обеспечить в больших программах. Локальные переменные исчезают при выходе из блока, поэтому вопрос обеспечения уникальности имен перестает существовать. Например, один программист использует переменную S типа float в качестве суммы ряда чисел, а другой обозначает переменной S строку символов типа AnsiString– локализация обеспечивает такую возможность.
Локальные и глобальные переменные
Как противоположность локальным, существуют и глобальные переменные – это такие значение, смысл и тип которых сохраняется при выходе из блока. Чтобы переменная приобрела бы характер глобальной, её надо описывать вне блока, в некоторой области, которая является общедоступной для всех блоков программы. Такой областью может быть заголовочный модуль программы (файл с расширением *.h – здесь этот вариант не рассматривается) либо текст программного модуля *.cpp, расположенный сразу после директив компилятора в интервале до первой процедуры, созданной самим Builder-ом (рис. 19).

Рис. 19. Описание глобальных и локальных переменных
Основной смысл создания глобальных переменных состоит в том, что они очень удобны для передачи данных между блоками и модулями: в одном блоке глобальной переменной присваивается значение, а другой блок эту переменную использует, при этом никаких дополнительных действий со стороны программиста не требуется.
Недостаток глобальных переменных заключается в том , что их не должно быть слишком много. Тогда программисту придется запоминать имена и роль каждой переменной, держать их в своей памяти, что усложняет программирование, удлиняет время составления программы и уменьшает надежность программирования.
Зона действия глобальных и локальных переменных определяется такими правилами: если имена глобальной и локальной переменной различны, то в блоке используется значение глобальной переменной; если их имена совпадают, то будет использовано значение локальной переменной. На рис. 20 приводится пример, который студент изучает самостоятельно.

{ AnsiString S; //в общем блоке описана глобальная строка S S="ABC";
….
….
{//блок1. Здесь строка S переопределена. Используется её локальное значение
AnsiString S; S="123";
ShowMessage(S);//Будет выведено значение "123"
}
…
…
{//блок2 Здесь строка S не переопределена. Сохраняется её глобальное значение
ShowMessage(S);//Будет выведено значение "ABC"
}
}// конец общего блока
Рис. 20. Пример областей действия глобальных и локальных переменных
Операторы выхода из блока return и break
К операторам безусловного перехода следует отнести ещё два оператора, которые в отличие от goto , отвечают требованиям структурного программированияэто return и break. Эти операторы предназначены для немедленного выхода из блока, их можно записать в любой точке программы. Команда return обеспечивает немедленное прекращение выполнения текущего модуля без анализа каких-либо условий и передачу управления операционной системе, оператор break обеспечивает выход их текущего блока в окружающий его блок, но не в операционное окружение. Более детально студент изучает эти операторы во время лабораторных работ.
Построение диалогового окна, функция MessageDlg |
|
Для организации интерактивного выбора одного из двух направлений |
в |
программе в систему Builder включены несколько специальных инструментов, одним из которых является функция MessageDlg (message dialog, то есть –сообщение с диалогом).
Назначение: организация диалога в форме сообщения с вариантами ответов в виде кнопок
Структура функции:
MessageDlg (<текст сообщения>, <тип окна>, <набор кнопок>,<номер контекста в help-файле>)

Действия системы: в центре экрана строится окно в соответствии с размером надписи и типом окна (операнд < тип окна >), в которое помещается строка символов (операнд <текст сообщения>), предлагаются кнопки для диалога (операнд <набор кнопок>) и прорисовываются другие элементы оформления
(рис. 21).
Эта функция была разработана для первых версий системы Builder, когда в языке программирования сохранялись ещё многие «старые» грамматические формы, поэтому запись этой функции не вполне согласуется с современной структурой языка C++.
Рассмотрим пример: мы хотим, чтобы в программе «Калькулятор плавающих чисел» при вводе не допустимого по форме числа выдавалось бы сообщение , представленное на рис. 21
Рис. 21. Результат работы функции MessageDlg
Не рассматривая вопрос о том, каким образом определить ошибку, приведем только запись собственно функции. По сути дела, эта функция представляет собою логическое выражение, которое удобно использовать для оператора if:
if (MessageDlg
("Ошибка при вводе данных. Подтвердите продолжение работы",//1 mtConfirmation,//2
TMsgDlgButtons() << mbYes << mbNo,//3 0)//4
== mrYes)//5 {//6
//инструкции группы 1
}
else {// 7
//инструкции группы 2
}
Рис. 22. Запись функции MessageDlg
Более кратким образом можно записать вызов функции MessageDlg так, как показано на рис. 23.
if(MessageDlg("Ошибка при вводе данных. Подтвердите продолжение работы", mtConfirmation,TMsgDlgButtons() << mbYes << mbNo,0)== mrYes)
Рис. 23. Краткая запись функции MessageDlg
Объяснение: Функция содержит 5 операндов (их номера записаны в поле комментариев на рис. 22):
1- текст сообщения. Он может быть представлен как строковая константа, либо как строковая переменная типа AnsiString. В примере – это константа "Ошибка при вводе данных. Подтвердите продолжение работы".
2-тип диалогового окна, указанного системной константой, у нас – mtConfirmation, то есть окно для подтверждения.
3- набор кнопок, прорисованных в окне. У насэто две кнопки с надписями
Yes и No (рис. |
21). Здесь в |
системный |
массив TMsgDlgButtons, |
предназначенный |
для хранения объектов «кнопки», передаются две кнопки, |
||
обозначенные константами mbYes |
и mbNo. Обратите внимание на редко |
||
используемые символы передачи данных <<. |
Этот тип записи перешел в |
язык C++ из «старых» версий без изменений.
4- номер контекста в help-файле, в данном случае – 0. Это условный номер раздела справочного файла, к которому произойдёт обращение, если пользователь нажмёт клавишу F1, когда диалоговое окно активно.
5- указатель кнопки, при выборе которой всё логическое выражение приобретёт значение true. В нашем случае выражение станет истинным, если выбрана (нажата ) кнопка Yes. Обратите внимание на знак сравнения – это
удвоенное равенство.
Исключительные ситуации, выявление ошибок , оператор try
Исключения и их стандартная обработка
При работе программы могут возникать различного рода ошибки: преобразование вводимой строки в число несовместимого типа, переполнение, деление на нуль, попытка открыть несуществующий файл и т.п. При возникновении таких ситуаций программа генерирует так называемое исключение и выполнение дальнейших вычислений в данном блоке прекращается, происходит аварийное завершение программы, как иногда говорят, «авост» - аварийный останов. Исключение— это событие специального вида, характеризующее возникшую в программе аварийную

ситуацию. Оно может также содержать в виде параметров некоторую уточняющую информацию.
Если исключение не перехвачено нигде в программе, то оно обрабатывается стандартным методом и программа завершается аварийно, при этом пользователю выдаётся краткая информация на английском языке
Естественно для надежной и дружественной программы такая ситуация недопустима, а значит необходимо уметь перехватывать и обрабатывать посвоему исключительные ситуации.
Собственная обработка исключений.
Для этой цели служит оператор try, имеющий большое разнообразие форм, из которых мы рассмотрим только одну.
Назначение: обработка исключительной (аварийной) ситуации
Структура функции: |
|
try {<оператор, могущий вызвать ошибку>} |
//1 |
catch (Exception *exception) |
//2 |
{<операторы собственной обработки исключительной ситуации >//3
}
//<следующие операторы> //4
Действия системы: производится попытка выполнить оператор в строке 1 (слово try переводится как «попытаться»). Если ошибка не произошла, то программа переходит к строке 4, пропуская строки 2 и 3. Если же при выполнении оператора 1 возникла исключительная ситуация, то стандартная системная реакция (системное сообщение и останов ) не вызывается, а вместо неё выполняются операторы собственного разработчика – строка 3. Строка 2 «catch (Exception *exception)» указывает на то, что перехватываются все известные варианты ошибок.
Вернемся к задаче «Калькулятор плавающих чисел» (рис. 21). Мы хотим выдавать сообщение, представленное на экране, в том случае, когда пользователь допустил ошибку при вводе, а именно, ввел десятичной число в недопустимой форме, допустим, вместо разделительной точки он ввел запятую.
Тогда фрагмент программы, обрабатывающей исключительную ситуацию, может быть таким:
{float a; |
//1 |
|
try {a=StrToFloat(Edit1->Text);} |
//2 |
|
catch(Exception *exception) |
//3 |
|
{if ( MessageDlg("Ошибка при вводе данных. Подтвердите продолжение работы",
mtConfirmation,TMsgDlgButtons() << mbYes << mbNo,0) == mrYes) |
//4 |
|
exit; |
//5 |
|
else Close();//6 |
|
}
// следующий операторы 7 }// конец блока
Рис. 24. Собственный обработчик ошибки преобразования
Если пользователь ввел в Edit1 правильное по форме плавающее число (оператор 1), то ошибки не будет, операторы 3,4,5 и 6 пропускаются и выполняется переход к группе 7. Если же при выполнении оператора 1 возникла ошибка, то машина не остановится, а управление перейдёт к группе 3. В результате на экране возникнет собственное сообщения на русском языке, представленное на рис. 21.
Вопросы для самопроверки
1.Как выполняются полный и сокращённый операторы if?
2.Почему не рекомендуется использовать оператор безусловного перехода goto?
3.В чем отличие оператора goto от других операторов безусловных переходов?
4.Для какой цели вводится оператор – переключатель (switch), если переходы любой сложности можно описать с помощью if и goto?
5.Как строятся сложные логические выражения из операций отношения?
6.Какие компоненты Builder ориентированы на выполнение логических функций?
7.Укажите роль операндов в операторе вывода диалогового окна командой MessgeDlg.
8.Как предотвратить аварийный останов программы при выполнении недопустимых в определенном контексте операций ?
9.Аргументируйте утверждение, что программный блок с описанием локальных переменных нельзя называть модулем.
10.Зачем в развитых языках программирования вводится механизм локализации переменных?
Тема 2.3. Программирование циклических алгоритмов с известным количеством повторений
Задачи такого рода являются, пожалуй, наиболее распространёнными. Они возникают при обработке элементов массивов с фиксированным количеством элементов, при определении значений математических целочисленных функции и т.п. Например, вычисление суммы натурального ряда чисел от 1 до N производится по формуле
n
S = iΣ=1i ,
которая в программировании выражается как итерационное вычисление вида:
Si= Si-1+i.
Если в качестве начального значения S принять 0, то за N шагов будет получен искомый результат.
Детально словесный алгоритм может быть описан так :
1.установить начальные значения S=0 и i=1;
2.проверить условие вхождения в цикл i<=N;
3.если условие ложно, то перейти к пункту 6;
4.если условие истинно, то выполнить вычисления по пункту 5;
5.S=S+i; i=i+1; и вернуться к пункту 2
6.вывести значение S (на экран или печать);
7.закончить вычисления.
Алгоритм, в котором есть последовательность операций (группа
инструкций), которая должна быть выполнена несколько раз, называется циклическим, а сама последовательность операций именуется циклом.
Для организации циклов может быть использовано сочетание условного оператора if и безусловного перехода goto.
Инструкция goto
Инструкция if используется для перехода к последовательности инструкций программы в зависимости от некоторого условия. Поэтому их иногда называют инструкциями условного перехода. Помимо этих инструкций управления ходом выполнения программы существует еще одна
— инструкция безусловного перехода goto. В общем виде оператор безусловного перехода goto записывается следующим образом:
goto <метка>,
где метка — это идентификатор, находящийся перед инструкцией, которая должна быть выполнена после инструкции goto.
В отличие от других переменных метку не надо объявлять в инструкциях описания. В программе метка ставится перед инструкцией, к которой должен быть выполнен переход в результате выполнения инструкции goto. Сразу после метки ставится двоеточие.
Организация циклов с помощью инструкций if и goto
Вышеописанный алгоритм может быть представлен так (рис.25): { int s,i;
i=1; s=0;
Cycle: if (i<=10) goto Nachalo; else goto Vyvod; Nachalo: S=S+i ; i=i+1; goto Cycle;
Vyvod:ShowMessage(“Сумма десяти натуральных чисел равна”+IntToStr(s)); }//конец блока
Рис. 25. Программа вычисления суммы десяти чисел натурального ряда
Логика работы такой маленькой программы проста и очевидна, однако применение оператора безусловного перехода goto в данном варианте практически неприемлемо по следующим причинам:
1.Оператор goto может передать управления в любую точку программы, и за этим нет и не может быть никакого формального контроля. Стало быть, легко совершить ошибку и программирование становится ненадежным. Например, можно войти в цикл, минуя его заголовок, когда значения переменных, управляющих циклом, не определено.
2.Когда программист видит метку в программе, то очень трудно установить, откуда, из какой другой точки программы сюда произошло обращение, следовательно, программа плохо читается, а её логика плохо прослеживается. Такие программы не структурированы.
3.Особенности машинной реализации оператора goto таковы, что быстродействие полученной программы оказывается существенно более низким по сравнению с организацией цикла оператором for.
В литературе по программированию можно встретить суждения о недопустимости применения инструкции goto, поскольку она приводит к запутанности программ. Однако с категоричностью таких утверждений согласиться нельзя. В некоторых случаях применение инструкции goto вполне оправдано.
Отмеченные недостатки применения оператора goto приводит к тому, что для организации циклов разрабатываются и применяются другие операторы.
Организация циклов с помощью инструкции for
Оператор с ключевым словом for предполагает наличие специальной переменной обязательно целого типа, называемой параметром цикла. Оператор for состоит из двух частей : заголовка цикла и тела цикла. Заголовок оператора for устанавливает начальное значение параметра цикла и его модификацию, а также проверяет условие входа в цикл. Тело цикла состоит из одного или многих операторов любого уровня сложности: в теле цикла могут находится условные операторы, переключатели или внутренние циклы.
Общая структура оператора for такова:
Назначение: организация циклов с известным количеством повторений
Структура:
for (<нач. значение параметра цикла >;<условие входа в цикл>; <модификатор параметра цикла > ) //1
{<операторы тела цикла>//2
}
//<следующие операторы> //3
Пример: for (i=1;i<=10,i++) {//Начало тела цикла //операторы тела цикла> }//Конец цикла
Выполнение.
Вход в цикл всегда производится только через его заголовокэто одно из требований принципа структурирования программ. При первом исполнении заголовка параметру цикла (в примере – переменной i) присваивается начальное значение ( 1 ). Проверяется условие входа в тело цикла (i<=10). Если условие истинно, происходит вхождение в тело цикла и выполняются все операторы вплоть до последней закрывающей фигурной скобки. В этой точке производится возврат к заголовку цикла и прежде всего модифицируется параметр цикла (i++), то есть значение параметра увеличивается на единицу. Затем вновь определяется истинность условия вхождения в цикл и, если оно истинно, исполняются операторы тела и процесс повторяется. Если условие окажется ложным, а это наступит, когда параметр i превысит значение 10, тело цикла пропускается и происходит переход к следующим операторам.
Приведем фрагмент той же программы для вычисления суммы ряда чисел, но с помощью оператора for (рис. 26).
{ int s,i s=0;
for (i=0;i<=10;i++)
{
S=S+i;
}
ShowMessage(“Сумма десяти натуральных чисел равна ”+IntToStr(s)); }//конец блока
Рис. 26. Вычисление суммы десяти чисел натурального ряда с помощью оператора for
Преимущества такого способа программирования очевидны. Кроме хорошо структурированной программы в качестве достоинства следует указать на то, что такая программа работает существенно быстрее, так как компилятор выбирает более рациональные машинные коды при трансляции оператора for.
Отметим некоторые особенности и ограничения оператора for:
• параметром цикла может быть только переменная целого типа;
•модификация параметра осуществляется только на плюс единицу или на минус единицу, другое значение шага не допустимо. В последнем случае заголовок цикла записывается так:
for (i=10; i>=1;i--)
Другой типичной задачей, приводящей к циклическим алгоритмам с известным количеством повторений, является обработка массивов. Предположим, задан массив X из 10 целых чисел, требуется найти сумму всех чисел, как говорят, сумму элементов массива. Этой задаче будет посвящена одна из лабораторных работ, поэтому здесь поместим только фрагмент программы без её детального обсуждения (рис. 27).
{ int s,i, x[10];
// установить значения элементов массива – см. лабораторные работы s=0;
for (i=0;i<=9;i++)
{
s=s+x(i);
}
ShowMessage(“Сумма десяти членов массива X равна ”+IntToStr(s)); }//конец блока
Рис. 27. Вычисление суммы десяти элементов массива X
Эта программа по структуре совпадает с предыдущей, разница лишь в том, что в первой программе аргументом вычислений был сам параметр цикла i, а здесь же он играет роль индекса элемента массива, непосредственно не участвуя в вычислениях.
Вопросы для самопроверки
1.В чем состоят достоинства оператора for по отношению к while ?
2.С какой целью используется оператор for, если любой цикл можно организовать с помощью операторов if и goto?
3.Если оператор for подразумевает организацию цикла с известным количеством повторений, то можно ли его применить к задаче поиска элемента в массиве, когда не всегда обязательно надо просматривать массив до конца?
4.Может ли телом оператора цикла быть оператор другого цикла?
5.В чем состоят отличия компонентов ListBox и Memo?
Тема 2.4. Программирование алгоритмов с подпрограммами
Понятие подпрограмм является одним из важнейших достижений в технологии программирования, поскольку использование подпрограмм способствует достижению следующих важнейших целей:
•надежности программирования в смысле уменьшения вероятности ошибок;
•увеличению производительности труда программиста;
•структурированию программ, легкости её составления и её читабельности.
При программировании в такой мощной среде, как Builder, модульность программы достигается автоматически за счет того, что Builder самостоятельно строит заготовки программных модулей. Однако в ряде случаев автоматическая генерация подпрограмм невозможна, поэтому следует освоить этот аспект программирования. Данному вопросу посвящена одна из программ, выполняемых в рамках лабораторных работ
(«Обработка строк»), здесь же будут рассмотрены общие идеи. |
|
Рассмотрим типовую задачу, приводящую к подпрограммам. |
|
Задача. Предположим, в трех различных точках программы |
мы |
обрабатываем данные о разных прямоугольниках со сторонами (a,b), (c,d) и (e,f). Числовые значения этих переменных известны, в каждой точке требуется определить площадь каждого прямоугольника (s1,s2,s3) и показать её на экране. Естественно, можно решить эту задачу традиционным способом
(рис. 28 ).
{int a,b,c,d,e,f,s1,s2,s3;
// установить значения a,b,c,d,e,f, s1=a*b;
ShowMessage(“Площадь прямоугольника со сторонами ”
+IntToStr(a)+” и “ +IntToStr(b)+” равна “+IntToStr(s1)); s2=c*d;
ShowMessage(“Площадь прямоугольника со сторонами ”
+IntToStr(c)+” и “ +IntToStr(d)+” равна “+IntToStr(s2)); s3=e*f;
ShowMessage(“Площадь прямоугольника со сторонами ”
+IntToStr(e)+” и “ +IntToStr(f)+” равна “+IntToStr(s3));
}
Рис. 28. Традиционная программа для определения площадей трех прямоугольников Вариант 1.
Легко увидеть, что одинаковые по существу вычисления повторяются многократно. Если, тем не менее, такое решение всё-таки приемлемо для простых вычислениё, то когда типовые вычисления займут несколько сотен

строк программного текста, то очевидно, что прямое программирование не рационально.
Подпрограмма в языке C++ оформляется в виде конструкции, называемой функцией, как собственно и поступает Builder.
Общий вид функции:
Назначение функции: оформление типового участка алгоритма как подпрограммы
Структура функции:
<тип вычисляемого значения><имя >(<список формальных параметров>) {//начало блока подпрограммы <операторы тела подпрограммы>
…return(<возвращаемое значение>); }//конец блока подпрограммы Примеры заголовков функции:
int |
Plochad(int dlina, int visota)// пример 1 |
void |
Plochad(int dlina, int visota)// пример 2 |
}//конец блока подпрограммы
Особенности:
Основное назначение функции – это участвовать в составе арифметических выражений, как если бы собственная функция была бы стандартной, как например sin(x). Это можно получить, если вместо имени функции подставлять значение, которое она вычисляет. Принято говорить, что функция возвращает значение. Тип этого значения и указывается перед её именем.
Впримере 1 предполагается, что функция Plochad возвращает в точку вызова целое значение, поэтому её тип указан как int.
Впримере 2 указан тип void, что означает, что функция не возвращает никаких значений: по-видимому, программист внутри тела функции выводит результаты на экран, а в вызывающей точке они не рассматриваются.
Список формальных параметров представляет собой перечень аргументов с указанием их типов, относительно которых построен вычислительный алгоритм. Элементы списка разделяются запятыми (но не точкой с запятой!)
На этапе описания функции ещё не существует значений аргументов, поэтому их и называют формальными. Можно полагать, что это просто поименованные ячейки памяти, в которые будут засланы рабочие значения при вызове процедуры. Список реальных значений, передаваемых процедуре,
называется списком фактических параметров. Их тип и количество должны совпадать с формальными параметрами. При вызове подпрограммы фактические аргументы будут подставлены повсюду в теле функции вместо формальных параметров, и алгоритм выполняется.
Если подпрограмма возвращает какое-либо значение в вызывающую точку, то в теле подпрограммы обязательно указывается оператор
return (<возвращаемое значение>);
Если функция обозначена как viod , то есть она ничего не возвращает, то оператор return не используется.
Терминологическое замечание
Влитературе по программированию часто делают различия в типах подпрограмм, называя их функциями или процедурами:
Функцией называют такую подпрограмму, которая возвращает в точку вызова одно и только одно значение.
Процедурой называют такую подпрограмму, которая возвращает в точку вызова много значений, одно значение или ни одного (например печать).
Функции и процедуры различаются по методу их вызова: процедуры играют роль самостоятельного оператора, тогда как функции являются членами выражения, наряду с другими операндами.
Вязыке C++ формальных различий между двумя типами подпрограмм не делается, но одна и та же функция может вызываться по-разному : либо как операнд (рис. 29), либо как оператор (рис. 30).
Составим программу для задачи о |
площади |
прямоугольника с |
использованием функции, возвращающей |
числовое |
значение площади |
(рис. 29) |
|
|
int Plochad(int dlina, int visota) //Объявление подпрограммы как функции. //Обратите внимание на оператор return, возвращающий вычисленное значение {return (dlina*visota);}//конец описания функции
//вызов функции в программе имеет вид операнда, записанного в правой //части оператора присваивания
{int a,b,c,d,e,f,s1,s2,s3; // a,b,c,d,e,f- стороны прямоугольников;
//s1,s2,s3-их площади
//установить значения a,b,c,d,e,f,
s1=Plochad(a,b);// вызов подпрограммы оформлен как операнд
ShowMessage(“Площадь прямоугольника с сторонами ”
+IntToStr(a)+” и “ +IntToStr(b)+” равна “+IntToStr(s1); s2=Plochad(c,d);
ShowMessage(“Площадь прямоугольника с сторонами ”
+IntToStr(a)+” и “ +IntToStr(b)+” равна “+IntToStr(s2); s3=Plochad(e,f);
ShowMessage(“Площадь прямоугольника с сторонами ”
+IntToStr(a)+” и “ +IntToStr(b)+” равна “+IntToStr(s3);
}
Рис. 29. Определения площадей трех прямоугольников с помощью подпрограммыфункции. Вариант 2
Такая программа выглядит несколько лучше предыдущей (рис. 28) , но все равно содержит многочисленные повторы. Для этой задачи рациональнее использовать функцию, не возвращающую никаких значений, то есть процедуру.
//Объявление подпрограммы как процедуры void Plochad(int dlina, int visota);
{int Pl;//вспомогательная локальная переменная для определения значения площади
Pl= dlina*visota;
ShowMessage(“Площадь прямоугольника с сторонами ”
+IntToStr(l)+” и “ +IntToStr(h)+” равна “+IntToStr(Pl));
}
//подпрогамма-процедура является самостоятельным оператором {int a,b,c,d,e,f; // стороны прямоугольников
//установить значения a,b,c,d,e,f,
Plochad(a,b); // вызов подпрограммы оформлен как самостоятельный оператор
Plochad(c,d);
Plochad(e,f);
}
Рис. 30. Определения площадей трех прямоугольников с помощью подпрограммыпроцедуры. Вариант 3
Как легко видно, последний вариант программы много рациональнее двух предыдущих.
Вопросы для самопроверки
1.С какой целью в язык программирования вводится конструкция «блок»?
2.Каким образом блок ограничивает зону действия переменной?
3.В чем состоит взаимодействие глобальных и локальных переменных?
4.Как выделяется память для переменных, описанных в блоке?
5.Можно ли считать блоком любую процедуру ?
6.Объясните механизм взаимодействия фактических и формальных параметров.
7.В чем заключается явление побочного эффекта процедур ?
8.В чём состоят достоинства и недостатки передачи фактического значения процедуре через глобальную переменную?
9.В чем заключается передача фактических параметров по «значению» и по «имени»? Укажите достоинства и недостатки каждого из способов.
10.Каковы общие и отличительные особенности функций и процедур?

Тема 2.5. Циклы с неизвестным количеством повторений
Алгоритмы такого класса чаще всего возникают в таких случаях:
•при расчете значений методом итераций (тригонометрические функции, извлечение квадратного корня);
•при поиске значений (в массивах, файлах, в базе данных и т.п. ).
Итерация и рекурсия
По своей логике итерационные алгоритмы достаточно сложны, поэтому здесь мы не будем их подробно рассматривать, этим темам посвящены лабораторные работы с соответствующими названиями. Ограничимся разъяснениями различия терминов «итерация» и «рекурсия».
Итерация предполагает такие последовательные вычисления, когда значение на n-м шаге вычисления определяется как функция от значения, полученного на предыдущем (n-1)-м шаге, например:
Sn = (n +1)S*n−(1n +2) .
На итерационных формулах такого рода базируются многие алгоритмы вычисления суммы сходящихся рядов.
Рекурсия означает, что вычислительная функция f(x)обращается сама к себе, внешне повторяя, таким образом, итерацию:
fn = K * fn−1 .
В математике эти понятия близки, однако в программировании они реализуются существенно различными способами. В итерационных методах предыдущее значение вычисления просто засылается в ту ячейку памяти, которая служит аргументом для последующего вычисления. Относительно этой ячейки и построен алгоритм. На очередном шаге предыдущее значение теряется, оно замещается новым, а количество ячеек памяти не изменяется и никаких особых расходов памяти не производится. Рекурсивный метод предполагает вызов функции из тела самой функции. Все компиляторы построены так, что при вызове функции их фактические параметры записываются в специальную область памяти, называемую стеком. Следовательно, длина стека растёт пропорционально номеру вызова, количество ячеек, занятых под стек, увеличивается, а значит, возникает угроза переполнения стека и аварийного останова машины.
Поэтому в программировании следует избегать рекурсивных вызовов
процедур. |
|
Остановимся только на |
наиболее распространенном варианте |
оператора цикла, специально предназначенного для организации.
Инструкция while
Инструкция while (цикл while) используется в том случае, если некоторую последовательность действий (инструкций программы) надо
выполнить несколько раз, причем необходимое число повторений во время разработки программы неизвестно и может быть определено только во время работы программы.
В общем виде инструкция while записывается следующим образом: while <условие>
{
// здесь инструкции, которые надо выполнить несколько раз
}
где условие — выражение логического типа, определяющее условие выполнения инструкций цикла.
Инструкция while выполняется следующим образом:
1.Сначала вычисляется значение выражения условие.
2.Если значение выражения условие равно false (условие не выполняется), то на этом выполнение инструкции while завершается и программа переходит к операторам, следующим за телом цикла.
3.Если значение выражения условие равно true (условие выполняется), то выполняются расположенные фигурными скобками инструкции тела цикла. После этого снова проверяется выполнение условия. Если условие выполняется, то инструкции цикла выполняются еще раз. И так до тех пор, пока условие не станет ложным (false).
Внимание!
Для того чтобы инструкции цикла while, которые находятся между фигурными скобками, были выполнены хотя бы один раз, необходимо, чтобы перед выполнением инструкции while значение выражения условие было истинно.
Для того чтобы цикл завершился, нужно, чтобы последовательность инструкций между фигурными скобками влияла на значение выражения условие (изменяла значения переменных, входящих в выражение условие).
Вопросы для самопроверки
1.В чем заключаются отличия в программировании итерационных алгоритмов от рекурсивных?
2.В чем состоят ограничения в использовании рекурсивных процедур?
3.Какие средства организации алгоритмов можно использовать для программирования итерационных циклов?
4.Какой метод трансляции – компиляция или интерпретациялучше приспособлен для итерационных алгоритмов?
5.Существуют ли ограничения на вид функции, корень которой определяется методом половинного деления? Если да, то какие?