Правила записи программы на языке Си 5 Правила формального описания синтаксиса языка программирования 6

Вид материалаЛекции

Содержание


10.4.Особенности работы с массивами большого размера
11.Модульное программирование в системе Turbo C
11.1.Обеспечение корректной стыковки модулей
Подобный материал:
1   ...   18   19   20   21   22   23   24   25   ...   28

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


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

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

Если эта схема неприемлема или если массив одномерный, то можно воспользоваться специальным атрибутом указателя 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

#include

#include

#include


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, то вся адресация будет выполняться по модулю равному размеру сегмента и результат будет неверным.

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


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

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

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

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

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

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


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

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

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

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

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

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