2812

Структура программы и модификаторы типа указателей в ОС MS-DOS

Лекция

Информатика, кибернетика и программирование

Лекция. Структура программы и модификаторы типа указателей в ОС MS-DOS В общем виде программа на языке C состоит из директив препроцессора, объявлений и определений объектов, команд, которые могут быть записаны как в одном, так и в нескольких моду...

Русский

2012-10-19

53.5 KB

4 чел.

Лекция 13

Структура программы и модификаторы типа указателей в ОС MS-DOS

В общем виде программа на языке C состоит из директив препроцессора, объявлений и определений объектов, команд, которые могут быть записаны как в одном, так и в нескольких модулях. В последнем случае при сборке программы создаётся файл проекта, в котором указываются имена всех входящих в проект файлов. Обязательно наличие одной и только одной функции main() в одном из файлов с исходными текстами. Функция main() задаёт точку входа в программу – с этой функции начинается и ей же заканчивается выполнение программы (в общем случае программа может быть завершена использованием системных функций завершения независимо от окончания функции main()).

Загрузочный модуль (типа .exe) ОС MS-DOS имеет структуру, показанную на рисунке 1.

Рис.1

Секция кода – содержит код программы (функции программиста и библиотечные функции).

Секция данных – содержит инициализируемые данные (глобальные и статические переменные) и неинициализируемые данные (константы).

Стек – используется для хранения автоматических объектов, передачи параметров и сохранения адреса возврата при вызове функции.

Ближняя куча (near heap) –динамически выделенная память.

Свободная память – память, не используемая программой.

После загрузки программы на исполнение положение и границы секций кода и данных фиксированы, начало кучи и стека (начало стека располагается снизу, а не сверху) также фиксированы, а их границы изменяются в процессе выполнения в зависимости от действий программы (направление увеличения их размеров показано стрелкой).

Регистр процессора – это ячейка памяти внутри процессора. Процессоры x86 для адресации в реальном режиме работы используют 4 сегментных регистра: CS (регистр кода, по нему происходит адресация кода программы), DS (регистр данных, по нему происходит адресация данных), SS (регистр стека, по нему производится адресация стека) и ES (дополнительный регистр, как правило дублирует регистр данных). Регистр указывает на определённый адрес в памяти, из которого можно обращаться к 64K (размер одного сегмента) ячейкам памяти по смещению относительно сегментного регистра (чтобы адресоваться к ячейкам памяти, лежащим за пределами доступных 64K, необходимо изменять значение сегментного регистра, что при программировании на языке C явно делать крайне нежелательно). Указатель называется ближним (near), если он содержит только значение смещения (сегментный адрес берётся из соответствующего сегментного регистра), данный указатель может адресоваться к 64K ячейкам памяти. Указатель называется дальним (far), если он содержит и сегментный адрес, и смещение относительно сегментного адреса, данный указатель может адресоваться к 1M ячейкам памяти. Указатель может быть указателем типа huge. Отличие указателей типа far от указателей типа huge состоит в том, что при выполнении арифметических операций с указателем типа far изменяется только смещение, сегментная часть остается постоянной, и указатель типа far может адресоваться только к 64K ячейкам памяти, чтобы его сместить, необходимо в программе явно изменять сегментную часть (проводить нормализацию), а при выполнении арифметических операций с указателями типа huge изменяться также будет и сегментная часть, поэтому можно адресоваться к любой ячейке в пределах 1M, такой указатель хранится в памяти в нормализованном виде.

Длину указателя определяет специальный модификатор (near, far или huge), используемый при объявлении указателя.

Следует учитывать, что одна ячейка памяти – это один байт. Если указатель указывает на объект, размер которого отличен от 1 байта, то в пределах одного сегмента указатель может обращаться не к 64K элементам, а к 64K/(размер типа) элементам. Например, указатель типа float в пределах сегмента будет обращаться к 64K/4=16K элементам, которые будут занимать 64K ячеек памяти.

Пример 1

float near *f;

int far *i;

char huge *c;

Модели памяти.

Особенности операционной системы MS-DOS требуют дополнительных уточнений относительно распределения памяти ЭВМ. Для этого определяются 6 моделей памяти.

Модель TINY.

Общий объём памяти для кода, данных и стека – 64K. Все указатели – ближние.

Модель SMALL.

Общий объём памяти для кода – 64K, для данных и стека – 64K. Все указатели по умолчанию – ближние, но для данных могут использоваться и дальние указатели. Начиная с этой модели в программе появляется возможность использования дальней кучи (far heap) для динамического использования памяти. К дальней куче могут обращаться только дальние указатели.

Модель MEDIUM.

Рекомендуется для больших программ с малым количеством статических данных. Общий объём памяти для кода каждого модуля – 64K, для данных и стека – 64K. Указатели данных по умолчанию – ближние, указатели функций по умолчанию – дальние.

Модель COMPACT.

Рекомендуется в случае с малым объёмом кода, но большим объёмом данных. Общий объём памяти для кода – 64K, для данных – 64K, для стека – 64K. Указатели данных по умолчанию – дальние, указатели функций по умолчанию – ближние. Начиная с этой модели отсутствует ближняя куча.

Модель LARGE.

Общий объём памяти для кода каждого модуля – 64K, для данных – 64K, для стека – 64K. Все указатели – дальние.

Модель HUGE.

Общий объём памяти для кода каждого модуля – 64K, для данных каждого модуля – 64K, для стека – 64K. Все указатели – дальние.

Данные модели памяти используются в среде Borland C++ 3.1 для ОС MS-DOS. Установка модели осуществляется в диалоговом окне Code Generation, вызываемом командой меню Options>Compiler>Code Generation.

Динамическое распределение памяти

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

Процесс работы с динамической памятью:

  •  получение динамической памяти заданного объёма;
  •  работа с данными, распределяемыми в динамической памяти (если необходимо, то возможно перераспределение выделенного объёма памяти с сохранением находящихся в нём данных);
  •  освобождение динамической памяти по окончании работы с данными.

Для получения блока динамической памяти используются функции malloc() и calloc(). Прототипы функций:

void* malloc(size_t size);

void* calloc(size_t nitems, size_t size);

Функция malloc() в качестве аргумента принимает размер запрашиваемого блока в байтах. Функция calloc() в качестве первого аргумента принимает число элементов, под которые необходимо выделить память, в качестве второго аргумента – размер одного элемента в байтах. Тип size_t аналогичен типу unsigned int. Функции в случае успешного выделения возвращают указатель на тип void, содержащий адрес выделенного блока, который нужно явно преобразовать к указателю на необходимый тип данных. В случае ошибки (как правило связанной с тем, что такого объёма свободной памяти нет) функции возвращают значение NULL. NULL – это стандартная константа языка C, которая обозначает нулевой указатель. После выполнения динамического распределения памяти необходимо обязательно проверять, какое значение возвратили функции. Если значения указателя – NULL, то работа с памятью невозможна (в большинстве случаев следует завершить программу).

Для перераспределения уже выделенного объёма памяти используется функция realloc(). Прототип функции:

void* realloc(void* block, size_t size);

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

Для освобождения блока динамической памяти используется функция free(). Прототип функции:

void free(void* block);

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

В среде Borland C++ 3.1 для работы с дальней кучей необходимо использовать дальние указатели и соответствующие функции, но с приставкой far.

Пример 2

#include <stdio.h>

#include <alloc.h>

#include <stdlib.h>

void main(void)

{

int n,m;

printf("Input string number\n");

scanf("%d",&n);

printf("Input column number\n");

scanf("%d",&m);

float *f,sum=0;

if((f=(float*)malloc(n*m*sizeof(float)))==NULL)

{

 printf("System do not have free memory\n");

 exit(1);

}

printf("Input matrix");

for(int i=0;i<n;i++)

 for(int j=0;j<m;j++)

 {

  printf("\nmatrix[%d][%d]=",i,j);

  scanf("%f",(f+i*m+j));

 }

if((f=(float*)realloc(f,(n+1)*m*sizeof(float)))==NULL)

{

 printf("System do not have free memory\n");

 exit(1);

}

for(int t=0;t<m;t++,sum=0)

{

 for(int j=0;j<n;j++)

  sum+=*(f+j*m+t);

 *(f+n*m+t)=sum;

}

for(int k=0;k<n+1;k++)

{

 printf("\n");

 for(int j=0;j<m;j++)

  printf("%f\t",*(f+k*m+j));

}

free(f);

}


 

А также другие работы, которые могут Вас заинтересовать

19538. Фильтры второго и высших порядков 452.79 KB
  1 Лекция 7. Фильтры второго и высших порядков Определение фильтра второго порядка Примером фильтра вторго порядка является фильтр . Рассматриваем только вещественный случай. Переходя к Z преобразованию получим: . Найдя корни многочлена в знаменателе пере
19539. Фильтры Баттеруорта 297.97 KB
  2 Лекция 8. Фильтры Баттеруорта Отыскание параметров фильтра В левой и правой частях в знаменателе находятся многочлены от переменной z. Найдем корни этих многочленов. Множество корней по построению инвариантно относительно замены . Для устойчивости фильтр...
19540. Осциллятор. FIR фильтры 500 KB
  3 Лекция 9. Осциллятор. FIR фильтры Полосовой фильтр на основе фильтра низких частот В предыдущей лекции было показано каким образом можно построить различные фильтры. Оказывается любой из таких фильтров можно получить на основе фильтра низких частот с помо...
19541. Квадратурный зеркальный фильтр 372.27 KB
  2 Лекция 10. Квадратурный зеркальный фильтр Проектирование FIR фильтра на основе аппроксимации Рассмотрим симметрический фильтр с передаточной функцией. 1 Пусть задана вещественная передаточная функция. Положим. В результате замены имеем взаимно од
19542. WaveLet- преобразования 322.83 KB
  2 Лекция 11. WaveLet преобразования WaveLetпреобразование является альтернативой преобразованию Фурье в тех случаях когда сигнал не носит периодического характера. Различают непрерывное и дискретное WaveLetпреобразования. Предполагается что все интегралы рассмот...
19543. Wavelet фильтрация 356.85 KB
  1 Лекция 12 Wavelet фильтрация Детализация сигнала Введем обозначение: для любой функции . Положим . Предложение. Если выполнено условие ортогональности то при фиксированном функции образуют ортонормированную систему. Доказательство. Имеем при . Нор...
19544. Шум от квантования сигнала 585.83 KB
  2 Лекция 13. Шум от квантования сигнала. Multiresolution переменная разрешающая способность Пусть справедливо дополнительное предположение: . Из включения вытекает представление где ортогональное дополнение пространства до пространства . При сделанных пре
19545. Быстрые схемы дискретного преобразования Фурье 515.42 KB
  2 Лекция 14. Быстрые схемы дискретного преобразования Фурье. Обычные формулы для вычисления ДПФ требуют большого количества умножений: где число точек в ДПФ. Существуют приемы позволяющие уменьшить это количество. Они называются быстрыми схемами БПФ. Пр
19546. Свертка последовательностей и ее вычисление 174.65 KB
  2 Лекция 15.Свертка последовательностей и ее вычисление Сдвиг последовательности Пусть имеется последовательность . Мы можем превратить ее в бесконечную последовательность положив . Выберем целое и определим . Найдем связь между преобразованиями Фурье э