Создание и использование модулей
Помимо использования стандартных библиотек язык C позволяет создавать пользователю-программисту собственные библиотеки функций. Это позволяет упростить процесс программирования и улучшить качество программ. Каждая библиотека оформляется как отдельный заголовочный файл с расширением h. В этом файле производится объявление констант, переменных, типов данных, функций Определение (описание) функций помещают в файле с таким же именем, но с расширением .cpp.
Для подключения созданной библиотеки (заголовочного файла) следует использовать директиву #include <имя заголовочного файла>.
Для добавления в проект заголовочного файла следует выбрать пункт меню Project и в раскрывшемся подменю выбрать пункт Add New Item.
В раскрывшемся окне следует выбрать шаблон Header File(.h) и в редакторе кода набрать код заголовочного файла.
После этого необходимо выполнить те же действия для создания файла с расширением .cpp и добавления его в проект (содержит описания функций). При этом в раскрывающемся окне следует выбрать пункт C++File(.cpp).
Объявления в отдельных частях модуля могут располагаться в любой последовательности и чередоваться при соблюдении правила использования имён: при объявлении нового имени (константы, типа, переменной, процедуры, функции) могут использоваться только ранее объявленные имена или имена из подключенных модулей.
Компилятор узнаёт о подключенных модулях, анализируя предложения использования в основной программе или в самих модулях. Предложение использования строится из ключевого слова #include и следующего за ним имени заголовочного файла. При создании нового модуля пользователь должен сам, при необходимости, добавить предложения использования. Например, в модуль, создаваемый для решения вычислительных задач, следует включить предложение использования со стандартным модулем math.h:
|
|
#include <math.h>
Присутствие имени модуля в предложении использования основной программы (или другого модуля) означает, что объявленные в нем константы, типы, переменные и подпрограммы доступны для использования в ней (или другом модуле).
Пример 1. Составить консольное приложение, выполняющее обработку матриц по формуле . Приложение должно использовать модуль modul1 и модуль modul2, подготовленные в одном проекте и хранящиеся в одной папке с основной программой. Модуль modul1 предназначен для объявления двух функций readmatr ввода матрицы и writematr вывода матрицы. Модуль modul2 должен содержать функцию addmatr сложения матриц и функцию mulmatr умножения матриц.
Требования к модулю modul1. Для хранения матриц следует использовать двумерные динамические массивы. Первый параметр функции readmatr должен представлять матрицу (int ***x – адрес переменной указателя, хранящего адрес массива указателей на строки матрицы), размеры которой должны быть заданы вторым и третьим параметрами, а четвёртый параметр целого типа с начальным значением 0 должен указывать режим работы функции. Функция readmatr должна обеспечить ввод матрицы с клавиатуры, если четвёртый параметр при её вызове опущен. Этот параметр предназначен для отладки программ, использующих модуль modul1. При задании в вызове функции readmatr четвёртого параметра, не равного 0, функция должна генерировать матрицу случайных чисел в диапазоне от 0 до абсолютного значения этого параметра включительно, причем если параметр меньше нуля, то при многократных запусках программы генерироваться должны разные данные, иначе – одни и те же данные. Сгенерированные матрицы должны выводиться в виде матрицы по строкам. Функция writematr должна иметь три параметра, первые два из которых задают размеры матрицы (количество строк и столбцов), а третий параметр представляет матрицу, и обеспечивать вывод матрицы в виде матрицы по строкам, разделяя числа не менее чем двумя пробелами.
|
|
Требования к модулю modul2. Для хранения матриц следует использовать двумерные динамические массивы. В функциях addmatr и mulmatr два первых параметра должны представлять только входные данные (матрицы, представляющие только исходные данные), а третий – только выходные данные (результирующую матрицу). Четвертые и пятые параметры задают количество строк и столбцов первой матрицы, шестой параметр функции mulmatr определяет количество столбцов второй матрицы.
|
|
Требования к основной программе. Использовать условную компиляцию, обеспечивающую при объявлении константы DEBUG директивой #define DEBUG 1, равной единице, генерацию случайных чисел для матриц, представляющих исходные данные, иначе – ввод матриц с клавиатуры (#if DEBUG==1).
Пример первый
Текст файла modul1.h (объявление функций ввода и вывода матриц):
void readmatr(int ***x,int m,int n,int r=0);
void writematr(const int m,const int n,int **x);
Текст файла modul2.h (объявление функций сложения и умножения матриц):
void addmatr(int **x,int **y,int ***z,int m,int n);
void mulmatr(int **x,int **y,int ***z,int m,int n,int l);
Текст файла modul1.cpp (описание функций ввода и вывода матриц):
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
void readmatr(int ***x,int m,int n,int r)
{ /*Ввод матрицы m*n в динамический массив
|
|
при r=0 - ввод с клавиатуры, иначе - от датчика случайных
чисел, причём при r>0 - без srand, иначе - с srand*/
int i,j;
//Установить размеры массива x
//равными размерам вводимой матрицы
*x=new int *[m]; //не было в С
for (int i=0;i<m;i++)
(*x)[i]=new int [n];
if (r==0)
{ //Ввод матрицы с клавиатуры
printf("\nwwedite elementi\n");
for (i=0; i<m; i++)
for (j=0; j<n; j++)
scanf("%d",&(*x)[i][j]);
}
else
{
//Генерация матрицы случайных целых чисел,
//из интервала 0..r
if (r<0) //При r<0 будет создан
//новый набор случайных чисел для матрицы
srand(time(NULL));
for (i=0; i<m; i++)
for (j=0; j<n; j++)
(*x)[i][j]=abs(r)*(float)rand()/RAND_MAX;
}
}
/*Вывод матрицы по строкам*/
void writematr(const int m,const int n,int **x)
{ for (int i=0;i<m;i++)
{
for (int j=0; j<n;j++)
printf("%4d ",x[i][j]);
printf("\n");
}
}
Текст файла modul2.cpp (описание функций сложения и умножения матриц):
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "math.h"
void addmatr(int **x,int **y,int ***z,int m,int n)
{//Функция сложения матриц Z=X+Y
int i,j;
//Установить размеры массива z равными размерам матрицы Z
*z=new int *[m];
for (int i=0;i<m;i++)
(*z)[i]=new int [n];
for (i=0; i<m; i++)
for (j=0; j<n; j++)
(*z)[i][j]=x[i][j]+y[i][j];
}
void mulmatr(int **x,int **y,int ***z,int m,int n,int l)
{//Функция умножения матриц Z=X*Y}
int i,j,k;
//Установить размеры массива z равными размерам матрицы Z
*z=new int *[m];
for (int i=0;i<m;i++)
(*z)[i]=new int [l];
for (i=0; i<m; i++)
for (j=0; j<l; j++)
{(*z)[i][j]=0;
for (k=0; k<n; k++)
(*z)[i][j]+=x[i][k]*y[k][j];
}
}
Текст основной программы
#include "stdafx.h"
#include "modul1.h"
#include "modul2.h"
#include <conio.h>
#include <stdio.h>
#define DEBUG 1
int _tmain(int argc, _TCHAR* argv[])
{ int **a,**b,**c,**d,**f,**e,**g;
//Если константа DEBUG объявлена директивой #define DEBUG 1, равной 1, то
//в исполняемую программу будут включены следующие операторы,
//обеспечивающие генерацию матриц случайных чисел и их вывод
#if DEBUG==1
printf("Matriza A 2x3 zelix sluchainix chisel \n");
readmatr(&a,2,3,8);
writematr(2,3,a);
printf("\n Matriza B 2x3 zelix sluchainix chisel \n");
readmatr(&b,2,3,-6);
writematr(2,3,b);
printf("\n Matriza C 3x4 zelix sluchainix chisel \n");
readmatr(&c,3,4,-9);
writematr(3,4,c);
printf("\n Matriza D 2x4 zelix sluchainix chisel \n");
readmatr(&d,2,4,9);
writematr(2,4,d);
#else
//иначе, то есть если константа DEBUG объявлена, не равной 1, то
//в исполняемую программу будут включены следующие операторы,
//обеспечивающие ввод матриц с клавиатуры.
printf("\n Wwedite matrizu A 2x3 zelix chisel\n");
readmatr(&a,2,3);
writematr(2,3,a);
printf("\n Wwedite matrizu B 2x3 zelix chisel\n");
readmatr(&b,2,3);
writematr(2,3,b);
printf("\n Wwedite matrizu C 3x4 zelix chisel \n");
readmatr(&c,3,4);
writematr(3,4,c);
printf("\n Wwedite matrizu D 2x4 zelix chisel \n");
readmatr(&d,2,4);
writematr(2,4,d);
#endif
getch();
//Вычисление и вывод матриц
addmatr(a,b,&f,2,3);
printf("\n Matriza F=(A+B) \n");
writematr(2,3,f);
mulmatr(f,c,&g,2,3,4);
printf("\n Matriza G=(A+B)xC \n");
writematr(2,4,g);
addmatr(g,d,&e,2,4);
printf("\n Matriza E=(A+B)xC+D\n");
writematr(2,4,e);
getch();
return 0;
}
При запуске программы с директивой, задающей значение константы DEBUG, равной единице, в окно программы сразу будут выведены с поясняющими текстами результаты построения матриц случайных чисел и вычисления выражения по шагам, как показано на рис. 7.1.
Рис.7.1.
При обращении к функции ввода с отрицательным последним параметром производится обращение к функции srand(time(NULL)); все матрицы будут при повторных запусках новые (используется каждый новое начальное значение случайного числа). При положительном значении последнего параметра все матрицы при повторных запусках не изменяются (используется одно и то же начальное значение случайного числа).
После отладки программы в строке с директивой #define DEBUG 1, в которой константе задается значение, равное 1, следует произвести изменение, задающее константе любое значение, отличное от 1.
В соответствии с условиями примера 1 для хранения исходных данных и результатов при сложении и умножении матриц используются разные массивы. С целью уменьшения числа дополнительных массивов (в программе это массивы f и g) можно сохранять полученные результаты на месте одного из исходных массивов. Для расширения возможностей функций addmatr и mulmatr можно создать новый модуль, заменяющий модуль modul2, или использовать его подпрограммы в новом модуле. Рассмотрим второй вариант, так как он интересен ещё и тем, как следует использовать одинаковые глобальные имена, объявленные в разных модулях.
Пример 2. Составить консольное приложение, выполняющее обработку матриц по формуле A∙B∙C+C+D∙A. Приложение должно использовать модули modul1 и modul2, подготовленные ранее (см. пример 1), и новый модуль modul3. Модуль modul3, как и модуль modul2, должен иметь функции addmatr и mulmatr, имеющие то же назначение, что и одноимённые функции модуля modul2, но, по возможности, использовать их и допускать применение одного и того же массива при обращении к ним одновременно в качестве разных фактических параметров.
Модуль ввода и вывода данных остается без изменений. В модуль modul2 внесено изменение, связанное с объявлением области действия имен. Это сделано для того, чтобы можно было обращаться к одноименным функциям, представленным в разных модулях.
Текст файла modul2.h (объявление функций сложения и умножения матриц):
namespace mod2
{void addmatr(int **x,int **y,int ***z,int m,int n);
void mulmatr(int **x,int **y,int ***z,int m,int n,int l);
}
Одноименные функции сложения и умножения матриц, объявленные в третьем модуле, позволяют сохранять полученный результат на месте одной из исходных матриц.
Текст файла modul3.h (объявление функций сложения и умножения матриц):
namespace mod3
{void addmatr(int **x,int **y,int ***z,int m,int n);
void mulmatr(int **x,int **y,int ***z,int m,int n,int l);
}
Текст файла modul2.cpp (описание функций сложения и умножения матриц):
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "math.h"
namespace mod2 {
void addmatr(int **x,int **y,int ***z,int m,int n)
{//Функция сложения матриц Z=X+Y}
int i,j;
//Установить размеры массива z равными размерам маирицы Z
*z=new int *[m];
for (int i=0;i<m;i++)
(*z)[i]=new int [n];
for (i=0; i<m; i++)
for (j=0; j<n; j++)
(*z)[i][j]=x[i][j]+y[i][j];
}
void mulmatr(int **x,int **y,int ***z,int m,int n,int l)
{//Процедура умножения матриц Z=X*Y}
int i,j,k;
//Установить размеры массива z равными размерам маирицы Z
*z=new int *[m];
for (int i=0;i<m;i++)
(*z)[i]=new int [l];
for (i=0; i<m; i++)
for (j=0; j<l; j++)
{(*z)[i][j]=0;
for (k=0; k<n; k++)
(*z)[i][j]+=x[i][k]*y[k][j];
}
}
}
Текст файла modul3.cpp (описание функций сложения и умножения матриц):
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "math.h"
#include "modul2.h"
namespace mod3 {
using namespace mod2;
void addmatr(int **x,int **y,int ***z,int m,int n)
{//Функция сложения матриц Z=X+Y}
int i,j;
//Выделить память по массив z, если его адрес не совпадает с адресом
// ни одного из исходных массивов
if (x!=(*z)&&y!=(*z))
{
*z=new int *[m];
for (int i=0;i<m;i++)
(*z)[i]=new int [n];
}
for (i=0; i<m; i++)
for (j=0; j<n; j++)
(*z)[i][j]=x[i][j]+y[i][j];
}
void mulmatr(int **x,int **y,int ***z,int m,int n,int l)
{//Процедура умножения матриц Z=X*Y}
int **w;
//Если адрес результирующего массива совпадает с адресом одного из
//исходных массивов, то используется промежуточный массив, память для //которого затем освоборждается
if (x==*z||y==*z)
{ mod2::mulmatr(x,y,&w,m,n,l);
for (int i=0;i<m;i++)
for (int j=0; j<l;j++)
(*z)[i][j]=w[i][j];
delete []w;
}
else mod2::mulmatr(x,y,z,m,n,l);
}
}
Текст основной программы
#include "stdafx.h"
#include "modul1.h"
#include "modul2.h"
#include "modul3.h"
#include <conio.h>
#include <stdio.h>
#define DEBUG 1
using namespace mod2;
using namespace mod3;
int _tmain(int argc, _TCHAR* argv[])
{ int **a,**b,**c,**d;
a=NULL;
b=NULL;
c=NULL;d=NULL;
// Если константа DEBUG объявлена директивой #define DEBUG 1, равной 1, то
//в исполняемую программу будут включены следующие операторы,
//обеспечивающие генерацию матриц случайных чисел и их вывод
#if DEBUG==1
printf("Matriza A 2x3 zelix sluchainix chisel \n");
readmatr(&a,2,3,18);
writematr(2,3,a);
printf("\n Matriza B 3x3 zelix sluchainix chisel \n");
readmatr(&b,3,3,6);
writematr(3,3,b);
printf("\n Matriza C 3x2 zelix sluchainix chisel \n");
readmatr(&c,3,2,9);
writematr(3,2,c);
printf("\n Matriza D 3x2 zelix sluchainix chisel \n");
readmatr(&d,3,2,9);
writematr(3,2,d);
#else
//иначе, то есть если константа DEBUG объявлена, не равной 1, то
//в исполняемую программу будут включены следующие операторы,
//обеспечивающие ввод матриц с клавиатуры.
printf("\n Wwedite matrizu A 2x3 zelix chisel\n");
readmatr(&a,2,3);
writematr(2,3,a);
printf("\n Wwedite matrizu B 3x3 zelix chisel\n");
readmatr(&b,3,3);
writematr(3,3,b);
printf("\n Wwedite matrizu C 3x2 zelix chisel \n");
readmatr(&c,3,2);
writematr(3,2,c);
printf("\n Wwedite matrizu D 3x2 zelix chisel \n");
readmatr(&d,3,2);
writematr(3,2,d);
#endif
getch();
//Вычисление и вывод матриц
mod3::mulmatr(a,b,&b,2,3,3);
printf("\n Matriza F=(AxB) \n");
writematr(2,3,b);
mod3::mulmatr(b,c,&b,2,3,2);
printf("\n Matriza G=AxBxC \n");
writematr(2,2,b);
mod3::addmatr(b,c,&b,2,2);
printf("\n Matriza E=(AxBxC)+C\n");
writematr(2,2,b);
mod2::mulmatr(a,d,&c,2,3,2);
printf("\n Matriza G=AxD \n");
writematr(2,2,c);
mod2::addmatr(b,c,&a,2,2);
printf("\n Matriza E=(AxBxC)+C+AxD\n");
writematr(2,2,a);
getch();
return 0;
}
Чтобы обеспечить возможность использования при вызове функции одного и того же имени массива в качестве параметра, используются функции, объявленные в третьем модуле. Если в функциях этого модуля выясняется, что адрес выходного фактического массива совпадает с адресом хотя бы одного входного фактического массива, то вызывается функция mulmatr модуля modul2 с указанием в качестве выходного параметра нового, объявленного в модуле modul3 массива, из которого, после возврата управления, данные копируются в выходной фактический параметр функции mulmatr модуля modul3. Если же в функции mulmatr модуля modul3 выясняется, что адрес выходного фактического массива не совпадает с адресами входных фактических массивов, то функция mulmatr модуля modul2 вызывается с тем же набором параметров, который был передан в функцию mulmatr модуля modul3.
|
if (x==*z||y==*z)
//Если
(x==*z //адрес выходного параметра равен адресу первого
|| //или
y==*z) //адресу второго входного параметра,
//значит выходной параметр совпадает
//с одним или обоими входными
При наличии в разных модулях одинаковых имён, при вызове функции следует в качестве префикса указывать соответствующую область действия имен (например, mod3::mulmatr(a,b,&b,2,3,3); или mod2::addmatr(b,c,&a,2,2);
Результат работы программы при вводе данных с клавиатуры представлен на рисунке рис. 7.2.
Рис.7.2.
При рассмотрении предметной области заданий на составление модулей авторы учитывали уровень знаний первокурсников, а также тот факт, что трудоемкость этого задания не должна существенно превышать трудоемкость других заданий. В связи с этим первая группа заданий включает задания на матрицы, а вторая группа заданий связана с решением геометрических задач. При решении задач первой группы студенты могут использовать фрагменты ранее написанных программ, модифицировав их в соответствии с требованиями оформления модулей и придав им более универсальный характер. Модуль, содержащий ряд часто используемых подпрограмм, может использоваться студентами в дальнейшем при изучении курса ”Информатики”, а также при выполнении заданий и по другим дисциплинам.
Дата добавления: 2018-10-27; просмотров: 510; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!