Программное изделие должно быть отдельным модулем, файл LAB2.C, в котором должны размещаться как данные (матрица и вспомогательная информация), так и функции, которые обеспечивают доступ. Внешний доступ к программам и данным модуля возможен только через вызов функций чтения и записи элементов матрицы. Доступные извне элементы программного модуля должны быть описаны в отдельном файле LAB2.H, который может включаться в программу пользователя оператором препроцессора:
#include "lab2.h"
Пользователю должен поставляться результат компиляции — файл LAB2.OBJ и файл LAB2.H.
Преобразование 2-компонентного адреса элемента матрицы, которую задает пользователь, в 1-компонентную должно выполняться отдельной функцией (так называемой, функцией линеаризации), вызов которой возможен только из функций модуля. Возможны три метода преобразования адреса:
u при создании матрицы для нее создается также и дескриптор D[N] — отдельный массив, каждый элемент которого соответствует одной строке матрицы; дескриптор заполняется значениями, подобранными так, чтобы: n = D[x] + y, где x, y — координаты пользователя (строка, столбец), n — линейная координата;
u линейная координата подсчитывается методом итерации как сумма полезных длин всех строк, предшествующих строке x, и к ней прибавляется смещение y-го полезного элемента относительно начала строки;
u для преобразования подбирается единое арифметическое выражение, которой реализует функцию: n = f(x,y).
Первый вариант обеспечивает быстрейший доступ к элементу матрицы, ибо требует наименьших расчетов при каждом доступе, но плата за это — дополнительные затраты памяти на дескриптор. Второй вариант — наихудший по всем показателям, ибо каждый доступ требует выполнения оператора цикла, а это и медленно, и занимает память. Третий вариант может быть компромиссом, он не требует дополнительной памяти и работает быстрее, чем второй. Но выражение для линеаризации тут будет сложнее, чем первом варианте, следовательно, и вычисляться будет медленнее.
В программном примере, который мы приводим ниже, полностью реализован именно третий вариант, но далее мы показываем и существенные фрагменты программного кода для реализации и двух других.
Описание логической структуры
Общие переменные
В файле LAB2.C описаны такие статические переменные:
u int NN — размерность матрицы;
u int SIZE —количество ненулевых элементов в матрице;
u int *m_addr —адрес сжатой матрицы в памяти, начальное значение этой переменной — NULL — признак того, что память не выделена;
u int L2_RESULT — общий флаг ошибки, если после выполнения любой функции он равен -1, то произошла ошибка.
Переменные SIZE и m_addr описаны вне функций с квалификатором static, это означает, что вони доступны для всех функций в этом модуле, но недоступны для внешних модулей. Переменная L2_RESULTтакже описана вне всех функций, не без явного квалификатора. Эта переменная доступна не только для этого модуля, но и для всех внешних модулей, если она в них буде описана с квалификатором extern. Такое описание имеется в файле LAB2.H.
Функция creat_matr
Функция creat_matrпредназначена для выделения в динамической памяти места для размещения сжатой матрицы. Прототип функции:
int creat_matr ( int N );
где N — размерность матрицы.
Функция сохраняет значение параметра в собственной статической переменной и подсчитывает необходимый размер памяти для размещения ненулевых элементов матрицы. Для выделения памяти используется библиотечная функция C malloc. Функция возвращает -1, если при выделении произошла ошибка, или 0, если выделение прошло нормально. При этом переменной L2_RESULT также присваивается значение 0 или -1.
Функция close_matr
Функция close_matr предназначена для освобождения памяти при завершении работы с матрицей,
Прототип функции:
int close_matr ( void );
Функция возвращает 0 при успешном освобождении, -1 — при попытке освободить невыделенную память.
Если адрес матрицы в памяти имеет значения NULL, это признак того, что память не выделялась, тогда функция возвращает -1, иначе — освобождает память при помощи библиотечной функции free и записывает адрес матрицы — NULL. Соответственно функция также устанавливает глобальный признак ошибки — L2_RESULT.
Функция read_matr
Функция read_matr предназначена для чтения элемента матрицы. Прототип функции:
int read_matr(int x, int y);
где x и y — координаты (строка и столбец). Функция возвращает значение соответствующего элемента матрицы. Если после выполнения функции значение переменной L2_RESULT -1, то это указывает на ошибку при обращении.
Проверка корректности задания координат выполняется обращением к функции ch_coord, если эта последняя возвращает ненулевое значение, выполнение read_matr на этом и заканчивается. Если же координаты заданы верно, то проверяется попадание заданного элемента в нулевой или ненулевой участок. Элемент находится в нулевом участке, если для него номер строки больше, чем номер столбца. Если элемент в нулевом участке, функция просто возвращает 0, иначе — вызывает функцию линеаризации lin и использует значение, которое возвращает lin, как индекс в массиве m_addr, по которому и выбирает то значения, которое возвращается.
Функция write_matr
Функция write_matr предназначена для записи элемента в матрицу. Прототип функции:
int write_matr(int x, int y, int value);
где x и y — координаты (строка и столбец), value — то значение, которое нужно записать. Функция возвращает значение параметра value, или 0 — если была попытка записи в нулевой участок. Если после выполнения функции значение переменной L2_RESULT -1, то это указывает на ошибку при обращении.
Выполнение функции подобно функции read_matrс тем отличием, что, если координаты указывают на ненулевой участок, то функция записывает value в массив m_addr.
Функция ch_coord
Функция ch_coord предназначена для проверки корректности задания координат. Эта функция описана как static и поэтому может вызываться только из этого же модуля. Прототип функции:
static char ch_coord(int x, int y);
где x и y — координаты (строка и столбец). Функция возвращает 0, если координаты верные, -1 — если неверные. Соответственно, функция также устанавливает значение глобальной переменной L2_RESULT.
Выполнение функции собственно состоит из проверки трех условий:
u адрес матрицы не должен быть NULL, то есть, матрица должна уже находиться в памяти;
u ни одна из координат не может быть меньше 0;
u ни одна из координат не может быть больше NN.
Если хотя бы одно из этих условий не выполняется, функция устанавливает признак ошибки.
Функция lin
Функция lin предназначена для преобразования двумерных координат в индекс в одномерном массиве. Эта функция описана как static и поэтому может вызываться только из этого же модуля. Прототип функции:
static int lin(int x, int y);
где x и y — координаты (строка и столбец). Функция возвращает координату в массиве m_addr.
Выражение, значение которого вычисляет и возвращает функция, подобрано вот из каких соображений. Пусть мы имеет такую матрицу, как показано ниже, и нам нужно найти линейную координату элемента, обозначенного буквой A с координатами (x,y):
x x x x x x
0 x x x x x
0 0 x x A x
0 0 0 x x x
0 0 0 0 x x
0 0 0 0 0 x
Координату элемента можно определить как:
n = SIZE — sizeX +offY,
где SIZE — общее количество элементов в матрице,
SIZE = NN * (NN — 1) / 2 + NN;
sizeX —количество ненулевых элементов, которые содержатся в строке x и ниже,
sizeX = (NN — x) * (NN — x — 1) / 2 + (NN — x);
offY — смещение нужного элемента от начала строки x,
offY = y — x
Программа пользователя
Для проверки функционирования нашего модуля создается программный модуль, который имитирует программу пользователя. Этот модуль обращается к функции creat_matr для создания матрицы нужного размера, заполняет ненулевую ее часть последовательно увеличивающимися числами, используя для этого функцию write_matr, и выводит матрицу на экран, используя для выборки ее элементов функцию read_matr. Далее в диалоговом режиме программа вводит запрос на свои действия и читает/пишет элементы матрицы с заданными координатами, обращаясь к функциям read_matr/ write_matr. Если пользователь захотел закончить работу, программа вызывает функцию close_matr.
Тексты программных модулей
/******* Файл LAB2.H *************************/
/* Описание функций и внешних переменных файла LAB2.C */
extern int L2_RESULT; /* Глобальна переменная — флаг ошибки */
/***** Выделение памяти под матрицу */
int creat_matr ( int N );
/***** Чтение элемента матрицы по заданным координатам */
int read_matr ( int x, int y );
/***** Запись элемент в матрицу по заданным координатам */
int write_matr ( int x, int y, int value );
/***** Уничтожение матрицы */
int close_matr ( void );
/******* Конец файла LAB2.H *******************/
/********** Файл LAB2.C *************************/
/* В этом файле определены функции и переменные для обработки матрицы, заполненной нулями ниже главной диагонали */
#include <alloc.h>
static int NN; /* Размерность матрицы */
static int SIZE; /* Размер памяти */
static int *m_addr=NULL; /* Адрес сжатой
матрицы */
static int lin(int, int); /* Описание функции линеаризации */
static char ch_coord(int, int); /* Описание функции проверки */
int L2_RESULT; /* Внешняя переменная, флаг ошибки */
/*************************************************/
/* Выделение памяти под сжатую матрицу */
int creat_matr ( int N ) {
/* N — размер матрицы */
NN=N;
SIZE=N*(N-1)/2+N;
if ((m_addr=(int *)malloc(SIZE*sizeof(int))) == NULL )
return L2_RESULT=-1;
else
return L2_RESULT=0;
/* Возвращает 0, если выделение прошло успешно, иначе -1 */
}
/*************************************************/
/* Уничтожение матрицы (освобождение памяти) */
int close_matr(void) {
if ( m_addr!=NULL ) {
free(m_addr);
m_addr=NULL;
return L2_RESULT=0;
}
else return L2_RESULT=-1;
/* Возвращает 0, если освобождение пршло успешно, иначе — -1 */
}
/*************************************************/
/* Чтение элемента матрицы по заданным координатам */
int read_matr(int x, int y) {
/* x, y -координати (строка, столбец) */
if ( ch_coord(x,y) ) return 0;
/* Если координаты попадают в нулевой участок — возвращается 0, иначе — применяется функция линеаризации */
return (x > y) ? 0 : m_addr[lin(x,y)];
/* Проверка успешности чтения — по переменной
L2_RESULT: 0 — без ошибок, -1 — была ошибка */
}
/*************************************************/
/* Запись элемента матрицы по заданным координатам */
int write_matr(int x, int y, int value) {
/* x, y -координати, value — записываемое значение */
if ( chcoord(x,y) ) return;
/* Если координаты попадают в нулевой участок — записи нет, иначе — применяется функция линеаризации */
if ( x > y ) return 0;
else return m_addr[lin(x,y)]=value;
/* Проверка успешности записи — по L2_RESULT */
}
/**************************************************/
/* Преобразование 2-мерных координат в линейную */
/* (вариант 3) */
static int lin(int x, int y) {
int n;
n=NN-x;
return SIZE-n*(n-1)/2-n+y-x;
}
/*************************************************/
/* Проверка корректности обращения */
static char ch_coord(int x, int y) {
if ( ( m_addr==NULL ) ||
( x>SIZE ) || ( y>SIZE ) || ( x<0 ) || ( y<0 ) )
/* Если матрица не размещена в памяти, или заданные координаты выходят за пределы матрицы */
return L2_RESULT=-1;
return L2_RESULT=0;
}
/*******Конец файла LAB2.C ********************/
/******** Файл MAIN2.C **************************/
/* "Программа пользователя" */
#include "lab2.h"
main(){
int R; /* размерность */
int i, j; /* номера строки и столбца */
int m; /* значения элемента */
int op; /* операция */
clrscr();
printf('Введите размерность матрицы >'); scanf("%d",R);
/* создание матрицы */
if ( creat_matr (R) ) {
printf("Ошибка создания матрицы\n");
exit(0);
}
/* заполнение матрицы */
for ( m=j=0; j<R; j++)
for ( i=о; i<R; i++)
write_matr(i,j,++m);
while(1) {
/* вывод матрицы на экран */
clrscr();
for (j=0; j<R; j++) {
for (i=0; i<R; i++)
printf("%3d ",read_matr(i,j));
printf("\n");
}
printf("0 — выход\n1 — чтение\n2 — запись\n>")
scanf("%d",&op);
switch(op) {
case 0:
if (close_matr()) printf("Ошибка при уничтожении\n");
else printf("Матрица уничтожена\n");
exit(0);
case 1: case 2:
printf("Введите номер строки >");
scanf("%d",&j);
printf("Введите номер столбца >");
scanf("%d",&i);
if (op==2) {
printf("Введите значение элемента >");
scanf("%d",&m);
write_matr(j,i,m);
if (L2_RESULT<0) pritnf("Ошибка записи\n");
}
else {
m=read_matr(j,i);
if (L2_RESULT<0) pritnf("Ошибка считывания\n");
else printf("Считано: %d\n",m);
}
printf("Нажмите клавишу\n"); getch();
break;
}
}
}
/*****Конец файла MAIN2.C **********************/
Варианты
Вариант 1 требует:
u добавления к общим статическим переменным еще переменной:
static int *D; /* адрес дескриптора */
u добавления такого блока в функцию creat_matr:
{
int i, s;
D=(int *)malloc(N*sizeof(int));
for (D[0]=0,s=NN-1,i=1; i<NN; i++)
D[i]=D[i-1]+s--;
}
u изменения функции lin на:
static int lin(int x, int y) {
return D[x]+y;
}
Вариант 2 требует:
u изменения функции lin на:
static int lin(int x, int y) {
int s;
for (s=j=0; j<x; j++)
s+=NN-j;
return s+y-x;
}
:
Лабораторная работа №4.
Проверка оборудования
Цель работы
Получение практических навыков в определении конфигурации и основных характеристик компьютера.
Постановка задачи
Для компьютера на своем рабочем месте определить:
u тип компьютера;
u конфигурацию оборудования;
u объем оперативной памяти;
u наличие и объем расширенной памяти;
u наличие дополнительных ПЗУ;
u версию операционной системы.
Пример решения задачи
Структура данных программы
Программа использует, так называемый, список оборудования — 2-байтное слово в области данных BIOS по адресу 0040:0010. Назначение разрядов списка оборудования такое:
u 0 установлен в 1, если есть НГМД
u 1 установлен в 1, если есть сопроцессор
u 2,3 число 16-Кбайтных блоков ОЗУ на системной плате
u 4,5 код видеоадаптера: 11 — MDA, 10 — CGA, 80 колонок, 01 — CGA, 40 колонок, 00 — другой
u 6,7 число НГМД-1 (если в разряде 0 единица)
u 8 9, если есть канал ПДП
u 9,10,11 число последовательных портов RS-232
u 12 1, если есть джойстик
u 13 1, если есть последовательный принтер
u 14,15 число параллельных принтеров
Структура программы
Программа состоит только из основной функции main(). Выделения фрагментов программы в отдельные процедуры не требуется, потому что нет таких операций, которые во время работы программы выполняются многократно.
Описание переменных
Переменные, применяемые в программе:
u type_PC — байт типа компьютера, записанный в ПЗУ BIOS по адресу FF00:0FFE;
u a, b — переменные для определения объема extended-памяти ПЭВМ, a — младший байт, b — старший байт;
u konf_b — 2-байтное слово из области данных BIOS, которое содержит список оборудования;
u type — массив символьных строк, представляющих типы компьютера;
u typ1A — массив байт, содержащий коды типов дисплеев;
u types1A[] —массив строк, содержащий названия типов дисплеев;
u j — вспомогательная переменная, которая используется для идентификации типа дисплея;
u seg — сегмент, в котором размещено дополнительное ПЗУ;
u mark — маркер ПЗУ;
u bufVGA[64] — буфер данных VGA, из которого (при наличии VGA) мы выбираем объем видеопамяти;
u rr и sr — переменные, которые используются для задания значения регистров общего назначения и сегментных регистров, соответственно, при вызове прерывания.