Создание и использование модулей



Помимо использования стандартных библиотек язык 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.

Рис. 6.2.
Проверка задания одного и того же массива в качестве выходного фактического параметра и одного или обоих входных фактических параметров при вызове функции mulmatr модуля modul2 выполняется в функции сравнением их адресов (имена массивов являются их адресами):

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; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!