Динамическая память, указатели, создание динамических массивов в Turbo Pascal. Работа с динамическими массивами (2 часа)
Цель работы
Ознакомление с понятиями динамическая память, динамическая переменная, указатель. Получение практических навыков написания и отладки программ в среде Turbo Pascal для работы с динамическими массивами.
Краткая теоретическая справка
Понятия динамических переменных
Все переменные, действующие в программе, должны быть перечислены в разделе описаний, причём каждая переменная должна иметь имя (идентификатор). При запуске программы транслятор, анализируя блок var раздела описаний, формирует специальные таблицы, в которые заносит данные о том, сколько памяти должно быть выделено каждой переменной, и реально выделяет соответствующее место в памяти, которое называют сегментом (областью) данных. При наличии в программе процедур и функций транслятор для входящих в них переменных заранее выделяет некоторый другой объём памяти, называемый стеком.
Память, выделенная переменным при активизации раздела описаний программы, сохраняется за ними на всё время работы программы. Поэтому такие переменные называют статическими. Все объявленные переменные размещаются в одной непрерывной области оперативной памяти (сегменте данных).
Динамической памятью называют оперативную память ЭВМ, предоставляемую программе при её работе, за вычетом сегмента данных (обычно 64 кбайт), стека (обычно 19 кбайт) и объёма, занимаемого самой программой. Динамически распределяемую область оперативной памяти называют кучей (heap).
Соответственно переменные, созданием и уничтожением которых может явно управлять программист, называют динамическими. Динамические переменные, количество и место расположения которых в памяти заранее неизвестно, невозможно обозначить идентификаторами. Поэтому единственным средством доступа к динамическим переменным являются указатели и ссылки на место их текущего расположения в памяти.
Разница между статическими и динамическими переменными в первую очередь заключается в обращении к ним. К статическим переменным обращаются по имени (идентификатору), к динамическим – по адресу.
Ссылочный тип данных и адресация
Динамические переменные в разделе описаний программы в явном виде не указывают, т.к. у них нет имени. Доступ к динамическим переменным осуществляется с помощью переменных специального типа данных, называемых ссылочными, а сами ссылочные переменные называют ссылками или указателями. Указатели могут быть типизированными и нетипизированными.
Типизированные указатели задают адреса области памяти, в которой хранятся или будут храниться данные базового, то есть заранее описанного стандартного или определённого программистом типа. Нетипизированные указатели задают адреса области памяти, в которой хранятся или будут храниться данные любого, заранее неизвестного типа.
Минимальной единицей памяти, которая может быть выделена под переменную или инструкцию программы, является байт. Каждый байт оперативной памяти имеет свой порядковый номер, называемый адресом.
Различные данные могут занимать в памяти один или несколько байт. Участок памяти, в котором хранится одно значение данных (один элемент данных), называют ячейкой памяти. Адресом ячейки является адрес её самого первого байта. Нумерация адресов начинается с нуля.
Структура объявления ссылочного типа данных выглядит следующим образом:
type
<имя типа ссылочной переменной> = ^<базовый тип д.п.>;
var
<имя д.п.> [, <имя д.п.>, ...] : <имя типа ссылочной переменной>;
<имя д.п.> [, <имя д.п.>, ...] : ^ <базовый тип д.п.> ;
Здесь д.п. – динамическая переменная. Базовым называют стандартный или заранее описанный тип данных.
Например, в разделе описаний программы (фрагмент приведен ниже) её строки описывают ссылочные типы и ссылочные переменные – указатели.
const
max = 150;
type
R = ^integer ;
T = ^real ;
A_mas = ^massiv;
massiv = array [1..max] of word;
var
a, b : R;
c : ^real;
d, f, g : T;
h, k : pointer;
m, n : A_mas;
Здесь R, T и A_mas – имена типов ссылочных (динамических) переменных целого (R) и вещественного (T) типов и типа массива (A_mas);
a и b – типизированные указатели (ссылочные переменные) на динамические переменные типа R, т.е. целого типа;
c, d, f и g – типизированные указатели на динамические переменные вещественного типа. Указатель "c" задан только в разделе переменных, а указатели d, f и g – через имя типа T;
m и n – типизированные указатели на динамические данные типа массива. При этом следует обратить внимание на то, что тип указателя massiv определён до определения базового типа array.
Перечисленные указатели называют типизированными, так как они связаны с адресами значений конкретных базовых типов.
Указатели h и k являются нетипизированными, так как описаны словом pointer, и могут ссылаться на данные любого типа.
В объявлениях ссылочных типов после символа "^" может стоять только простое имя типа, например, real, integer, string, но не string [15] или array[1..7] of integer.
В случае сложных имён типов используют переопределение типов, то есть сначала определяют одним словом тип, являющийся сложным, например, массивом, а затем этот тип используют с символом "^".
Создание и удаление динамических переменных
Для работы с динамическими переменными в модуле System языка Турбо Паскаль предусмотрено несколько процедур и функций.
В случае типизированных указателей динамические переменные создают с помощью процедуры new(p), которая выделяет в динамической памяти столько байтов, сколько требуется для хранения переменной заданного указателем р типа, а указателю р присваивается адрес первого байта выделенной в памяти ячейки.
В случае нетипизированных указателей динамические переменные создают с помощью процедуры getmem(p, size), которая выделяет в динамической памяти столько байтов, сколько указано переменной size, а указателю р присваивается адрес первого байта выделенной в памяти ячейки.
Для освобождения динамически выделенной для типизированных указателей памяти можно использовать процедуру dispose(p), которая очищает память, указанную ссылкой р.
В случае нетипизированных указателей для очистки динамической памяти предусмотрена процедура freemem(p, size), которая освобождает size байтов, начиная с адреса р.
После очищения (освобождения) указатели сохраняют свои значения, однако при этом указывают уже на несуществующие динамические переменные. Поэтому рекомендуется всем высвободившимся указателям присвоить значение nil.
При выделении динамической памяти полезными являются функции memavail и maxavail, первая из которых возвращает общий размер свободной динамической памяти в байтах, а вторая – размер наибольшего непрерывного участка свободной памяти.
Например,
var
p: ^real;
. . . . .
if maxavail > sizeof (real) then new(p);
. . . . .
Динамические массивы
Рассмотрим работу с динамическими массивами на примере программы перемножения прямоугольных матриц, размер которых заранее не известен, но может быть определён динамически.
Размеры перемножаемых матриц должны быть согласованы, а именно количество столбцов первого сомножителя должно равняться количеству строк второго.
Так как размер переменных заранее не известен, то процедуры new и dispose применить нельзя.
Для данной задачи подходят стандартные процедуры getmem и freemem.
Процедура getmem (var d: pointer; m: word) создаёт динамическую переменную указанного размера и присваивает значение адреса ссылочной переменной d. Если в памяти ЭВМ не хватает места для размещения переменной, то появляется сообщение об ошибке.
Процедура freemem (var d: pointer; m: word) как и процедура dispose освобождает память, отведённую под динамическую переменную, и делает эту память доступной для другого использования. Точно также после этого указателю d обязательно нужно присвоить значение nil.
Пример 1.
program Mult_Matr;
uses Crt;
type
Index_Of_Row= array [1..100] of ^VectColumn;
VectColumn= array [1..100] of integer;{здесь Index_Of_Row – имя типа ссылочных переменных, имеющих базовый тип динамического массива, VectColumn – имя типа ссылочной переменной типа "массив целых чисел"}
matrix= record {matrix – переменная типа "запись"}
N_rows, N_column: word; {Номер строки (ряда), номер столбца}
index: ^Index_Of_Row; {index – ссылка (указатель) на вектор-столбец VectColumn, длина которого равна числу строк матрицы Index_Of_Row. Каждый элемент столбца VectColumn сам является указателем на вектор-строку}
end;
var
A, B, C:matrix; {А и В – исходные матрицы, С – матрица – результат перемножения матриц А и В}
IndexSize, ElemSize, I, K:word; (* Размеры индекса и элемента матрицы, I, K – счётчики. Ссылка на элемент матрицы А имеет вид: A.index ^[I]^[K] *)
M, N:longint; (* Размеры матрицы *)
procedure CreateMatrix( var A: matrix; r c: word ); {Процедура создания матрицы}
var
j, k: word;
begin
with A do
Begin
N_rows:= r; N_column:= c;
j:= N_rows * SizeOf (Pointer);
inc ( IndexSize, j );
GetMem (index^[j], k);
for k := 1 to N_column do
index^[j]^[k] := -5 + random (11);
end;
end;
Begin
………………..
End.
Не нашли, что искали? Воспользуйтесь поиском по сайту:
©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.
|