ТОП 10:

Особенности работы с массивами большого размера



Массивами большого размера будем называть такие массивы, которые не помещаются в сегменте памяти.

Для больших двумерных массивов вариантом решения проблемы может служить предложенная в предыдущем разделе схема массива в виде вектора указателей на строки.

Если эта схема неприемлема или если массив одномерный, то можно воспользоваться специальным атрибутом указателя huge, который имеется у всех компиляторов, ориентированных на IBM PC.

Следует иметь в виду, что использование модели Large или указателей типа far недостаточно для корректной работы с большими массивами. Это происходит потому, что, во-первых, при выполнении действий над far указателями их сегментная часть не меняется, во-вторых, описанные выше функции выделения памяти не могут выделить память больше одного сегмента.

При работе с массивами большого размера, соответствующий указатель должен описываться с ключевым словом huge, даже в модели памяти Huge (указатели по умолчанию - far), например:

 

double huge *A;

 

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

Для работы с большими блоками памяти используются специальные функции с префиксом far.

Функция выделения памяти:

 

void far *farmalloc(unsigned long size);

 

Выделение памяти с обнулением:

 

void far *farcalloc(unsigned long nitems,

unsigned long size);

 

Изменение размера ранее выделенного блока памяти:

 

void far *farrealloc(void far *block,

unsigned long nbytes);

 

Освобождение блока памяти:

 

void farfree(void far *block);

 

Получение информации о верхнем свободном блоке памяти:

 

unsigned long farcoreleft(void);

 

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

Следующая программа иллюстрирует использование массива размером большим максимального размера сегмента:

 

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#include <malloc.h>

 

void far FarMalloc( unsigned long size )

{

void far *p = farmalloc(size);

if( !p )

{ printf("Недостаточно памяти!\n"); exit(1); }

return p;

}

 

void main(void)

{

double huge *A;

unsigned long i, maxN;

/* Выделение максимального блока памяти */

A = (double huge *) FarMalloc( maxN = farcoreleft() );

maxN /= sizeof(double);

printf("Размер массива: %lu\n", maxN);

getch();

/* Заполняем массив */

for(i = 0; i < maxN; i++) A[i] = i;

/* Печатаем часть массива.*/

for(i = 0; i < 1000; i++)

{

printf("%10.3lf ", A[i]);

if( (i + 6) % 5 == 0 ) printf("\n");

if( (i + 121) % 120 == 0 ) { getch(); clrscr(); }

}

printf("\n");

/* Освобождение памяти */

farfree(A);

}

 

Если в этой программе поменять атрибут huge на far, то вся адресация будет выполняться по модулю равному размеру сегмента и результат будет неверным.

Модульное программирование в системе Turbo C

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

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

Каждый файл исходного текста программы на языке СИ после компиляции образует отдельный модуль.

Программа собирается из отдельных модулей, находящихся на диске, и библиотечных модулей с помощью специальной программы, называемой компоновщиком или редактором связей. Компоновщик можно вызвать явно из командной строки MS DOS, или неявно из интегрированной среды разработки программ.

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

Обеспечение корректной стыковки модулей

Основной проблемой модульного программирования является обеспечение корректной стыковки отдельных модулей. Язык СИ имеет все необходимые для этого средства. Однако, ни один компилятор языка СИ не в состоянии отследить ошибки стыковки модулей, если программист не создал всех необходимых для этого условий. Рассмотрим основные правила организации модулей при программировании на языке СИ.

Каждый модуль программы должен описываться двумя файлами. Первый файл должен содержать текст функций модуля, то есть реализацию модуля и обычно имеет расширение *.c. Второй файл содержит описание интерфейса модуля, то есть прототипы функций модуля и описания глобальных переменных модуля с помощью оператора extern. Второй файл обычно имеет расширение *.h и называется заголовочным файлом.

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

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

Файл с исходным текстом функций модуля (реализацией модуля) должен содержать директиву препроцессора #include подключающую заголовочный файл, описывающий интерфейс данного модуля. Это необходимо для того, чтобы компилятор был в состоянии проверить соответствие интерфейса модуля его реализации.

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

Создание библиотек функций

При разработке программных проектов, состоящих из большого числа модулей, целесообразно пользоваться библиотеками модулей. Каждая такая библиотека обычно имеет расширение *.lib и может быть включена в проект с помощью средств поддержки проекта.

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

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

Для создания библиотек функций используются программы, называемые библиотекарями. В системе программирования фирмы Borland библиотекарь имеет имя tlib и может быть вызван из командной строки следующим образом:

 

"tlib " имя_библ ["/C"] ["/E"] команды [, файл_огл]

 

где имя_библиотеки - имя файла с создаваемой или модифицируемой библиотекой;

команды - последовательность команд модифицирующих библиотеку;

файл_огл - имя файла, в который будет помещено оглавление библиотеки;

/C - ключ, при наличии которого библиотекарь различает прописные и строчные буквы в именах внешних функций;

/E - клич, при наличии которого библиотекарь создает расширенный словарь.

Каждая команда модификации библиотеки имеет следующую форму:

 

 

("+" | "-" | "*" | "-+" | "-*") имя_модуля

 

где имя_модуля - имя скомпилированного модуля без расширения.

Символ "+" добавляет новый модуль в библиотеку, символ "-" удаляет модуль из библиотеки, символ "*" извлекает модуль из библиотеки без его удаления, символы "-+" или "+-" заменяют модуль в библиотеке, символы "-*" или "*-" извлекают и удаляют модуль из библиотеки.

Например, следующий вызов библиотекаря

 

tlib graph +line +draw +point, graph

 

создаст в текущем каталоге новую библиотеку с именем graph.lib, в которую будут добавлены модули line.obj, draw.obj и point.obj. Кроме того будет создан файл с оглавлением библиотеки, имеющий имя graph.lst.







Последнее изменение этой страницы: 2016-04-08; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.214.224.224 (0.007 с.)