Лабораторная работа 3

Принципы модульного программирования.

Массивы, строки, подпрограммы.

1 Общие сведения о модульном программировании

Модульность в языках программирования — принцип, согласно которому программное средство (ПС, программа, библиотека, веб-приложение и др.) разделяется на отдельные именованные сущности, называемые модулями. Модульность часто является средством упрощения задачи проектирования ПС и распределения процесса разработки ПС между группами разработчиков. При разбиении ПС на модули для каждого модуля указывается реализуемая им функциональность, а также связи с другими модулями.

Роль модулей могут играть структуры данных, библиотеки функций, классы, сервисы и др. программные единицы, реализующие некоторую функциональность и предоставляющие интерфейс к ней.

2 Структура проекта в C++ Builder

2.1 Структура файла проекта

Для каждого приложения C++ Builder создается один файл проекта, один make-файл и один файл ресурсов. Файл проекта генерируется при выборе пункта меню File/New Application. Первоначально файлу проекта присваивается по умолчанию имя Project1.cpp. Если в процессе разработки приложения добавляются формы и модули, C++ Builder обновляет файл проекта.

Для просмотра файла проекта следует выбрать пункт меню View/Project Source. Эта операция выполнит загрузку исходного текста файла проекта в редактор кода (рис. 1).

Рис. 1. Просмотр файла проекта в редакторе кода

Файл проекта имеет такую же структуру, как и файл модуля. Подобно файлу модуля, это файл исходного кода на языке C++, который компилируется с другими файлами при создании исполняемого файла.

НЕ нашли? Не то? Что вы ищете?

В файле проекта имеется определенный набор ключевых элементов:

- Директива препроцессора #include <vcl\vcl. h> предназначена для включения в текст проекта заголовочного файла, ссылающегося на описания классов библиотеки компонентов.

- Директива препроцессора #pragma hrdstop предназначена для ограничения списка заголовочных файлов, доступных для предварительной компиляции.

- Директива USEFORM сообщает, какие модули и формы используются в проекте.

- Директива USERES компилятора присоединяет файлы ресурсов к выполняемому файлу. При создании проекта автоматически создается файл ресурсов с расширением *.res для хранения курсоров, пиктограммы приложения и др.

- Application->Initialize() Это утверждение критично только в случае, если приложение является OLE automation-сервером. В остальных случаях оно фактически ничего не делает.

- Application->CreateForm() Это утверждение создает форму приложения. По умолчанию, каждая форма в приложении имеет свое утверждение CreateForm.

- Application->Run() Это утверждение запускает приложение (точнее, переводит его в состояние ожидания наступления одного из событий, на которое оно должно реагировать).

- Конструкция try...catch используется для корректного завершения приложения в случае возникновения ошибки при инициализации, создании форм, запуске приложения.

2.2 Структура модуля

Модули являются основой создания библиотек и приложений в C++ Builder. Модуль содержит исходный текст на языке C++ и первоначально представляет собой файл с расширением *.CPP. В дальнейшем каждый такой файл компилируется в объектный файл с расширением *.OBJ. Объектные файлы, в свою очередь, собираются компоновщиком в выполняемый файл с расширением *.EXE.

При добавлении к проекту новой формы генерируется новый модуль. При добавлении модуля к проекту при помощи выбора пункта меню File/New Unit создается пустая структура модуля, в которой включены директивы: #include <vcl\vcl. h>; #pragma hdrstop; #include "Unit2.h"

Директива #include "Unit2.h" указывает на то, что в текст модуля должен быть включен соответствующий заголовочный файл.

При создании модуля используются следующие правила:

Имя должно быть уникальным. Два модуля с одним и тем же именем не могут использоваться одновременно в одном и том же проекте.

Если модуль связан с формой, то имя исходного файла модуля и файла формы (с расширением *.dfm) должны быть одинаковыми. При создании обработчика событий в инспекторе объектов в тексте файла модуля генерируется шаблон функции, в который разработчик должен вводить код, выполняемый при наступлении обрабатываемого события.

2.3 Структура h-файла

h-файл генерируется при создании нового модуля. В нем содержится информация о данных и функциях, которые можно использовать в модуле. h-файл для модуля, связанного с формой, содержит описания интерфейсных элементов и других компонентов этой формы и обработчиков событий для них (то есть, в терминологии объектно-ориентированного программирования, описание класса формы). Такие описания автоматически добавляются в h-файл при внесении в форму новых компонентов или генерации новых обработчиков событий. Иными словами, в h-файле содержится интерфейс, а в самом модуле - реализация.

Рис. 3. Пример структуры h-файла

Примечание. При удалении из формы компонентов их описания удаляются из h-файла, а описания обработчиков событий сохраняются. При переименовании компонентов изменяются их описания в h-файле, а также имена и описания обработчиков событий, сгенерированные автоматически. Однако при этом не изменяются ссылки на эти компоненты и обработчики событий, используемые в других функциях, поэтому рекомендуется переименовывать компоненты и обработчики событий сразу же после их создания, пока на них не появились ссылки.

Не рекомендуется удалять из модуля пустые функции (например, случайно созданные шаблоны обработчиков событий). Они не увеличат размер исполняемого файла, но их удаление может привести к невозможности заново сгенерировать обработчик события, если в этом возникнет необходимость.

Отметим, что в модуле могут содержаться функции, не описанные в h-файле, однако видимость их в этом случае ограничивается данным модулем.

Внутри модуля функции могут быть определены и ссылаться друг на друга в произвольном порядке. Если данный модуль ссылается на другие формы и модули, следует с помощью директивы препроцессора #include включить в него соответствующий h-файл с помощью пункта меню File/Include Unit Hdr... . После этого интерфейсные элементы другого модуля будут доступны в данном модуле.

2.4 Файл формы

Форма является одним из важнейших элементов приложения C++ Builder. Процесс редактирования формы происходит при добавлении к форме компонентов, изменении их свойств, создании обработчиков событий.

Рис. 4. Структура файла формы

Когда к проекту добавляется новая форма, создаются три отдельных файла:

Файл модуля (.cpp) - cодержит исходный код, связанный с формой.

h-файл(.h) - cодержит описание класса формы, то есть описания содержащихся на форме компонентов и обработчиков событий.

Файл формы (.dfm) - двоичный файл, содержащий сведения об опубликованных (то есть доступных в инспекторе объектов) свойствах компонентов, содержащихся в форме. Двоичный файл формы содержит информацию, используемую для конструирования формы из компонентов, расположенных на ней. При добавлении компонента к форме и заголовочный файл, и двоичный файл формы модифицируются. При редактировании свойств компонента в инспекторе объектов эти изменения сохраняются в двоичном файле формы.

Хотя в C++ Builder файл .dfm сохраняется в двоичном формате, его содержание можно просмотреть с помощью редактора кода. Для этого нужно нажать правую клавишу мыши над формой и из контекстного меню формы выбрать пункт View as Text.

Отметим, что при изъятии какого-либо компонента с формы в буфер обмена в последнем реально оказывается часть тестового представления файла формы, содержащая описание данного компонента. В этом можно убедиться, выполнив затем операцию вставки из буфера обмена в любом текстовом редакторе. Модули Delphi 2.0

C++ Builder создан на основе визуальной библиотеки компонентов Borland Delphi, ставшей за последние два года весьма популярной среди разработчиков. По этой причине этот продукт имеет общую с Delphi библиотеку классов, часть из которых осталась написанной на Object Pascal. Из этого следует, что в приложениях можно использовать компоненты, созданные для Delphi 2.0.

Однако совместимость с Delphi этим не исчерпывается. В проектах C++ Builder можно использовать не только библиотеку компонентов Delphi, но и код, написанный на Object Pascal, а также формы и модули Delphi. Эти возможности появились благодаря включению в С++ Builder обоих компиляторов -- С++ и Object Pascal.

Рис. 5. Типы файлов, используемые в проектах С++ Builder

В соответствии с этим в качестве частей проекта могут быть использованы модули, написанные на Object Pascal. В этом можно убедиться, взяв формы какого-нибудь из примеров, созданных в Delphi 2.0 и включив их в проект С++ Builder. Возьмем в качестве такого примера приложение Graphex из набора примеров Delphi 2.0. Создадим новый проект, удалим из него созданную по умолчанию форму и добавим два модуля из приложения Graphex - Graphwin. pas и Bmpdlg. pas.

Рис. 6. Добавление модулей Delphi к проекту.

Скомпилируем проект. Можно убедиться в работоспособности полученного приложения, запустив и протестировав его.

В случае использования форм Delphi к проекту добавляются два файла - файл формы с расширением *.dfm и файл модуля с расширением *.pas. Описание класса формы содержится в самом модуле, следовательно, для этого не требуется отдельный файл заголовка.

Рассмотрим более подробно структуру модуля Delphi. В качестве простейшего примера приведем структуру модуля, связанного с формой, содержащей единственный интерфейсный элемент - кнопку закрытия окна: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin close; end; end.

Основными элементами модуля являются:

Заголовок модуля - предназначен для идентификации модуля и должен быть уникальным для данного проекта. Совпадает с именем файла с расширением *.pas.

Секция интерфейса (начинается зарезервированным словом interface). Секция интерфейса является местом, где объявляются константы, типы, переменные, процедуры* и функции, которые доступны для использования другими программными модулями. При объявлении процедур и функций в секции интерфейса необходимыми являются только их заголовки. Тела, или реализации, этих процедур и функций располагаются в секции реализации.

Секция реализации (начинается зарезервированным словом implementation) содержит процедуры и функции, включая обработчики событий. Объявления обработчиков событий должны присутствовать в секции интерфейса. Помимо этого, секция реализации может иметь дополнительные процедуры, функции и объявления констант, типов и переменных, упоминания о которых нет в секции интерфейса, но эти объявления видимы только в пределах данного модуля.

Секции инициализации и завершения (начинаются с зарезервированных слов initialization и finalization) - необязательные секции для размещения операторов, выполняемых соответственно при запуске и завершении приложения. Предложение uses (необязательное) содержит имена других модулей, используемых данным модулем и может располагаться непосредственно за ключевым словом interface и за ключевым словом implementation. Предложение uses делает секцию интерфейса других модулей доступной в данном модуле.

Отметим, однако, что для создания новых модулей Delphi требуется наличие Delphi. В С++ Builder их можно только использовать. Возможности редактирования форм и модулей Delphi в С++ Builder существенно ограничены - допустимы только операции, не затрагивающие секцию интерфейса модуля. Например, возможно перемещение интерфейсных элементов в пределах содержащих их компонентов-контейнеров, изменение размеров, копирование в буфер обмена, но запрещено их добавление или удаление. Возможны изменения внутри кода процедур и функций, но запрещено добавление новых обработчиков событий.

3 Массивы

Массив — это набор переменных одного типа, имеющих одно и то же имя. Доступ к конкретному элементу массива осуществляется с помощью индекса. В языке С все массивы располагаются в отдельной непрерывной области памяти. Первый элемент массива располагается по самому меньшему адресу, а последний — по самому большому. Массивы могут быть одномерными и многомерными. Строка представляет собой массив символьных переменных, заканчивающийся специальным нулевым символом, это наиболее распространенный тип массива.

Одномерные массивы

Общая форма объявления одномерного массива имеет следующий вид:

тип имя_переменной [размер];

Как и другие переменные, массив должен быть объявлен явно, чтобы компилятор выделил для него определенную область памяти (т. е. разместил массив). Здесь тип обозначает базовый тип массива, являющийся типом каждого элемента. Размер задает количество элементов массива. Например, следующий оператор объявляет массив из 100 элементов типа double под именем balance:

double balance[100];

Доступ к элементу массива осуществляется с помощью имени массива и индекса. Индекс элемента массива помещается в квадратных скобках после имени. Например, оператор

balance[3] = 12.23;

присваивает 3-му элементу массива balance значение 12.23.

Индекс первого элемента любого массива в языке С равен нулю. Поэтому оператор

char p[10];

объявляет массив символов из 10 элементов — от р[0] до р[9]. В следующей программе вычисляются значения элементов массива целого типа с индексами от 0 до 99:

#include <stdio. h>

int main(void)

{

int x[100]; /* объявление массива 100 целых */

int t;

/* присваение массиву значений от 0 до 99 */

for(t=0; t<100; ++t) x[t] = t;

/* вывод на экран содержимого x */

for(t=0; t<100; ++t) printf("%d ", x[t]);

return 0;

}

Двухмерные массивы

Стандартом С определены многомерные массивы. Простейшая форма многомерного массива — двухмерный массив. Двухмерный массив — это массив одномерных массивов. Объявление двухмерного массива d с размерами 10 на 20 выглядит следующим образом:

int d[10][20];

Во многих языках измерения массива отделяются друг от друга запятой. В языке С каждое измерение заключено в свои квадратные скобки.

Аналогично обращению к элементу одномерного массива, обращение к элементу с индексами 1 и 2 двухмерного массива d выглядит так:

d[1][2]

Многомерные массивы

В языке С можно пользоваться массивами, размерность которых больше двух. Общая форма объявления многомерного массива следующая:

тип имя_массива [Размер1][Размер2]...[РазмерN];

4 Строки

Одномерный массив наиболее часто применяется в виде строки символов. Строка — это одномерный массив символов, заканчивающийся нулевым символом. В языке С признаком окончания строки (нулевым символом) служит символ '\0'. Таким образом, строка содержит символы, составляющие строку, а также нулевой символ. Это единственный вид строки, определенный в С.

Объявляя массив символов, предназначенный для хранения строки, необходимо предусмотреть место для нуля, т. е. указать его размер в объявлении на один символ больше, чем наибольшее предполагаемое количество символов. Например, объявление массива str, предназначенного для хранения строки из 10 символов, должно выглядеть так:

char str[11];

Последний, 11-й байт предназначен для нулевого символа.

Записанная в тексте программы строка символов, заключенных в двойные кавычки, является строковой константой, например,

"некоторая строка"

В конец строковой константы компилятор автоматически добавляет нулевой символ.

Для обработки строк в С определено много различных библиотечных функций. Чаще всего используются следующие функции:

Имя функции

Выполняемое действие

strcpy(s1,s2)

Копирование s2 в s1

strcat(s1,s2)

Конкатенация (присоединение) s2 в конец s1

strlen(s1)

Возвращает длину строки s1

strcmp(s1,s2)

Возвращает 0, если s1 и s2 совпадают, отрицательное значение, если s1<s2 и положительное значение, если s1>s2

strchr(s1,ch)

Возвращает указатель на первое вхождение символа ch в строку s1

strstr(s1,s2)

Возвращает указатель на первое вхождение строки s2 в строку s1

Эти функции объявлены в заголовочном файле <string. h>.

5 Объявление функций в C++

В языке программирования С++ предусмотрена возможность создания собственных функций. В языке программирования С++ есть два типа функций:

– Функции, которые не возвращают значений

– Функции, возвращающие значение

Функции, не возвращающие значения, завершив свою работу никакого значения, не возвращают. Рассмотрим структуру объявления таких функций.

1

2

3

4

5

// структура объявления функций не возвращающих значений

void /*имя функции*/(/*параметры функции*/) // заголовок функции

{

// тело функции

}

Строка 2 начинается с зарезервированного слова void - это тип данных, который не может хранить какие-либо данные. Тип данных void говорит о том, что данная функция не возвращает никаких значений. void никак по-другому не используется и нужен только для того, чтобы компилятор мог определить тип функции. После зарезервированного слова void пишется имя функции. Сразу за именем функции ставятся две круглые скобочки, открывающаяся и закрывающаяся. Если нужно функции передавать какие-то данные, то внутри круглых скобочек объявляются параметры функции, они отделяются друг от друга запятыми. Строка 2 называется заголовком функции. После заголовка функции пишутся две фигурные скобочки, внутри которых находится алгоритм, называемый телом функции. Разработаем программу, в которой объявим функцию нахождения факториала, причём функция не должна возвращать значение.

// struct_func. cpp: определяет точку входа для консольного приложения.

#include "stdafx. h"

#include <iostream>

using namespace std;

// объявление функции нахождения n!

void faktorial(int numb)// заголовок функции

{

 int rezult = 1; // инициализируем переменную rezult значением 1

 for (int i = 1; i <= numb; i++) // цикл вычисления значения n!

 rezult *= i; // накапливаем произведение в переменной rezult

 cout << numb << "! = " << rezult << endl; // печать значения n!

}

int main(int argc, char* argv[])

{

 int digit; // переменная для хранения значения n!

 cout << "Enter number: ";

 cin >> digit;

 faktorial(digit);// запуск функции нахождения факториала

 system("pause");

 return 0;

}

После того, как были подключены все необходимые заголовочные файлы, можно объявлять функцию нахождения факториала. Под объявлением функции подразумевается выбор имени функции, определение параметров функции и написание алгоритма, который является телом функции. После выполнения этих действий функцию можно использовать в программе. Так как функция не должна возвращать значение, то тип возвращаемых данных должен быть void. Имя функции - faktorial, внутри круглых скобочек объявлена переменная numb типа int. Эта переменная является параметром функции faktorial(). Таким образом, все объявления в строке 8 в совокупности составляют заголовок функции. Строки 9 — 14 составляют тело функции faktorial(). Внутри тела в строке 10 объявлена переменная rezult, которая будет хранить результат нахождения n! После чего, в строках 11-12 Объявлен оператор цикла for для нахождения факториала. В строке 13 объявлен оператор cout, с помощью которого значение факториала будет печататься на экране. Теперь, когда функция объявлена можно воспользоваться ею. В строке 21 запускается функция faktorial(digit), внутри скобочек функции передаётся аргумент, т. е. значение, содержащееся в переменной digit.

Функции, возвращающие значение, по завершению своей работы возвращают определённый результат. Такие функции могут возвращать значение любого типа данных. Структура функций, возвращающих значение будет немного отличатся от структуры функций рассмотренных ранее.

1

2

3

4

5

6

// структура объявления функций возвращающих значения

/*возвращаемый тип данных*/ /*имя функции*/(/*параметры функции*/) // заголовок функции

{

// тело функции

 return /*возвращаемое значение*/;

}

Структура объявления функций осталась почти неизменной, за исключением двух строк. В заголовке функции сначала нужно определять возвращаемый тип данных, это может быть тип данных int, если необходимо возвратить целое число или тип данных float - для чисел с плавающей точкой. В общем, любой другой тип данных, всё зависит от того, что функция должна вернуть. Так как функция должна вернуть значение, то для этого должен быть предусмотрен специальный механизм, как в строке 5. C C помощью зарезервированного слова return  можно вернуть значение, по завершении работы функции. Всё, что нужно, так это указать переменную, содержащую нужное значение, или некоторое значение, после оператора return. Тип данных возвращаемого значения в строке 5 должен совпадать с типом данных в строке 2. Переделаем программу нахождения факториала так, чтобы функция faktorial() возвращала значение факториала.

// struct_func. cpp: определяет точку входа для консольного приложения.

#include "stdafx. h"

#include <iostream>

using namespace std;

// объявление функции нахождения n!

int faktorial(int numb)// заголовок функции

{

 int rezult = 1; // инициализируем переменную rezult значением 1

 for (int i = 1; i <= numb; i++) // цикл вычисления значения n!

 rezult *= i; // накапливаем произведение в переменной rezult

 return rezult; // передаём значение факториала в главную функцию

}

int main(int argc, char* argv[])

{

 int digit; // переменная для хранения значения n!

 cout << "Enter number: ";

 cin >> digit;

 cout << digit << "! = " << faktorial(digit) << endl;// запуск функции нахождения факториала

 system("pause");

 return 0;

}

Теперь функция faktorial() имеет возвращаемый тип данных - int, так как n! - это целое число. В строке 13 объявлен оператор return, который возвращает значение, содержащееся в переменной rezult. В строке 22 выполняем запуск функции faktorial(), возвращаемое значение которой отправляем в поток вывода с помощью оператора cout. Можно было бы написать так int fakt = faktorial(digit); — переменной типа int присваиваем возвращаемое значение функции faktorial(), после чего в переменной fakt будет храниться значение n!.

Задание на лабораторную работу

Задание 1

Написать программу нахождения минимального и максимального элементов заданной последовательности чисел. Вывести на экран значения и порядковые номера искомых элементов.

Задание 2

Написать программу подсчета суммарного числа букв 'а' и букв 'b' в заданной строковой переменной. Вывести на экран каких букв больше.

Задание 3

Написать программу поиска подстроки в заданной строке. В случае нахождения заданной подстроки необходимо удалить ее.

Задание 4

Даны действительные числа a, b. Получить u = min(a, b-a), y = min(ab, a+b),

k = min(u+v2, 3.14).

Вычисление u, v и k оформите в виде функций.

Задание 5

Вычислить

Описание: http://*****/books/pascal-tasks/pic7_3/image004.gif

Вычисление факториала числа оформите в виде подпрограммы.