мета-данные страницы
  •  
Загрузка не удалась. Возможно, проблемы с правами доступа?

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
pascal:pointers [21/11/2009 18:12] ocapascal:pointers [21/11/2009 18:16] (текущий) oca
Строка 2: Строка 2:
 FIXME FIXME
  
 +===== Использование указателей в Паскале =====
 +
 +http://www.lsnnet.ru/pascal/ispolzovanie-ukazatelej-v-paskale.html
 +
 +Подведем некоторые итоги. Итак, динамическая память составляет 200…300 Кбайт или больше, ее начало хранится в переменной HEAPORG, а конец соответствует адресу переменной HEAPEND. Текущий адрес свободного участка динамической памяти хранится в указателе HEAPPTR.
 +
 +Посмотрим, как можно использовать динамическую память для размещения крупных массивов данных. Пусть, например, требуется обеспечить доступ к элементам прямоугольной матрицы 100×200 типа EXTENDED. Для размещения такого массива требуется память 200000 байт (100*200*10).
 +
 +Казалось бы, эту проблему можно решить следующим образом:
 +
 +var
 +
 +i,j : Integer;
 +
 +PtrArr : array [1..100, 1...200] of Real;
 +
 +begin
 +
 +for i := 1 to 100 do
 +
 +for j := 1 to 200 do
 +
 +new(PtrArr[i,j]);
 +
 +…….
 +
 +end.
 +
 +Теперь к любому элементу вновь созданного динамического массива можно обратиться по адресу, например:
 +
 +PtrArr[1,1] := 0;
 +
 +if PtrArr[i,j*2] > 1 then ……
 +
 +Вспомним, однако, что длина внутреннего представления указателя составляет 4 байта, поэтому для размещения массива PTRARR потребуется 100*200*4 = 80000 байт, что превышает размер сегмента данных (65536 байт), доступный, как уже отмечалось, программе для статического размещения данных.
 +
 +Выходом из положения могла бы послужить адресная арифметика, т.е. арифметика над указателями, потому что в этом случае можно было бы отказаться от создания массива указателей PTRARR. и вычислять адрес любого элемента прямоугольной матрицы непосредственно перед обращением к нему. Однако в Турбо Паскале над указателями не определены никакие операции, кроме операций присвоения и отношения.
 +
 +Тем не менее, решить указанную задачу все-таки можно. Как мы уже знаем, любой указатель состоит из двух слов типа WORD, в которых хранятся сегмент и смещение. В Турбо Паскале определены две встроенные функции типа WORD, позволяющие получить содержимое этих слов:
 +
 +SEG(X) – возвращает сегментную часть адреса;
 +
 +OFS(X) – возвращает смещение.
 +
 +Аргументом X при обращении к этим функциям может служить любая переменная, з том числе и та, на которую указывает указатель. Например, если имеем
 +
 +var
 +
 +р : Real;
 +
 +begin
 +
 +……
 +
 +new(p);
 +
 +р := 3.14;
 +
 +…….
 +
 +end.
 +
 +то функция SEG(P) вернет сегментную часть адреса, по которому располагается 4-байтный указатель Р, в то время как SEG(P^) – сегмент 6-байтного участка кучи, в котором хранится число 3.14 .
 +
 +С другой стороны, с помощью встроенной функции
 +
 +PTR(SEG,OFS: WORD): POINTER
 +
 +можно создать значение указателя, совместимое с указателями любого типа. Таким образом возможна такая последовательность действий. Вначале процедурой GETMEM из кучи забираются несколько фрагментов подходящей длины (напомню, что за одно обращение к процедуре можно зарезервировать не более 65521 байт динамической памяти). Для рассматриваемого примера удобно резервировать фрагменты такой длины, чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е. 100 * 10 = 2000 байт. Начало каждого фрагмента, т.е. фактически начало размещения в памяти каждой строки, запоминается в массиве PTRSTR, состоящем из 100 указателей, теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель:
 +
 +var
 +
 +i,j:Integer;
 +
 +PtrStr : array [1..100] of pointer;
 +
 +const
 +
 +SizeOfReal = 6;
 +
 +begin
 +
 +for i := 1 to 100 do
 +
 +GetMem (PtrStr [i] , SizeOfReal*200) ;
 +
 +…….
 +
 +{Обращение к элементу матрицы [i,j]}
 +
 +pr := ptr(seg(PtrStr[i]),
 +
 +ofs(PtrStr[i])+(j-1)*SizeOfReal);
 +
 +if рr > 1 then
 +
 +…….
 +
 +end.
 +
 +Поскольку оператор вычисления адреса PR := PTR… будет, судя по всему, использоваться в программе неоднократно, полезно ввести вспомогательную функцию GETR, возвращающую значение элемента матрицы, и процедуру PUTR, устанавливающую новое значение элемента (правила объявления процедур и функций изложены в гл. 8). Каждая из них, в свою очередь, обращается к функции ADDRR для вычисления адреса. В примере 6.1 приводится программа, создающая в памяти матрицу из NxM случайных чисел и вычисляющая их среднее значение.
 +
 +Пример
 +
 +const
 +
 +SizeOfReal = 6; {Длина переменной типа REAL}
 +
 +N = 100; {Количество столбцов}
 +
 +М = 200; {Количество строк}
 +
 +var
 +
 +i,j : Integer;
 +
 +PtrStr: array [1..N] of pointer;.
 +
 +s : Real ;
 +
 +type
 +
 +RealPoint =^Real;
 +
 +{——————-}
 +
 +Function AddrR(i,j: word): RealPoint;
 +
 +{По сегменту i и смещению j выдает адрес вещественной переменной}
 +
 +begin
 +
 +AddrR := ptr(seg (PtrStr [i]),
 +
 +ofs{ PtrStr [i]) + (j -1) * SizeOfReal)
 +
 +end {AddrR} ;
 +
 +{——————-}
 +
 +Function GetR(i,j: Integer): Real;
 +
 +{Выдает значение вещественной переменной по сегменту i и смещению j ее адреса}
 +
 +begin
 +
 +GetR := AddrR(i,j)
 +
 +end {GetR};
 +
 +{——————-}
 +
 +Procepure PutR(i,j : Integer; x: Real);
 +
 +{Помещает в переменную, адрес которой имеет сегмент i и смещение j, вещественное значение x}
 +
 +begin
 +
 +AddrR ( i , j ) : = x
 +
 +end {PutR};
 +
 +{——————-}
 +
 +begin {Main}
 +
 +for i :=1 to N do
 +
 +begin
 +
 +GetMem (PtrStr [i] , M*SizeOfReal) ;
 +
 +for j := 1 to M do PutR(i, j, Random)
 +
 +end;
 +
 +S := 0;
 +
 +for i := 1 to N do
 +
 +for j := 1 to M do
 +
 +s := s +GetR(i,j);
 +
 +WriteLn(s/(N * М) :12:10)
 +
 +end {Main} .
 +
 +В рассмотренном примере предполагается, что каждая строка размещается в куче, начиная с границы параграфа, и смещение для каждого указателя PTRSTR равно нулю. В действительности при последовательных обращениях к процедуре GETMEM начало очередного фрагмента следует сразу за концом предыдущего и может не попасть на границу сегмента. В результате, при размещении фрагментов максимальной длины (65521 байт) может возникнуть переполнение при вычислении смещения последнего байта.
  
 ===== Процедуры и функции для работы с динамической памятью ===== ===== Процедуры и функции для работы с динамической памятью =====
Строка 74: Строка 263:
  
 MAXAVAIL MAXAVAIL
 +
 +Результат имеет тип LONGINT. За один вызов процедуры NEW или GETMEM нельзя зарезервировать памяти больше, чем значение, возвращаемое этой функцией.
 +
 +Функция MEMAVAIL.
 +
 +Возвращает размер в байтах общего свободного пространства кучи. Обращение:
 +
 +MEMAVAIL
 +
 +Результат имеет тип LONGINT.
 +
 +Процедура NEW.
 +
 +Резервирует фрагмент кучи для размещения переменной. Обращение:
 +
 +NEW ( ТР )
 +
 +Здесь ТР – типизированный указатель.
 +
 +За одно обращение к процедуре можно зарезервировать не более 65521 байта динамической памяти. Если нет свободной памяти требуемого размера, возникает ошибка периода исполнения. Если память не фрагментирована, последовательные обращения к процедуре будут резервировать последовательные участки памяти, так что начало следующего будет располагаться сразу за концом предыдущего.
 +
 +Процедура NEW может вызываться как функция. В этом случае параметром обращения к ней является тип переменной, размещаемой в куче, а функция NEW возвращает значение типа указатель. Например:
 +
 +type
 +
 +PInt =^Integer;
 +
 +var
 +
 +p: Pint;
 +
 +begin
 +
 +p := New(PInt);
 +
 +……
 +
 +end.
 +
 +При размещении в динамической памяти объекта разрешается в качестве второго параметра обращения к NEW указывать имя конструктора (см. гл.10).
 +
 +Функция OFS.
 +
 +Возвращает значение типа WORD, содержащее смещение адреса указанного объекта. Вызов:
 +
 +OFS ( X )
 +
 +Здесь Х- выражение любого типа или имя процедуры.
 +
 +Функция PTR.
 +
 +Возвращает значение типа POINTER по заданному сегменту SEG и смещению OFS. Вызов:
 +
 +PTR ( SEG, OFS )
 +
 +Здесь SEG – выражение типа WORD, содержащее сегмент;
 +
 +OFS – выражение типа WORD, содержащее смещение.
 +
 +Значение, возвращаемое функцией, совместимо с указателем любого типа.
 +
 +Процедура RELEASE.
 +
 +Освобождает участок кучи. Обращение:
 +
 +RELEASE ( PTR )
 +
 +Здесь PTR – указатель любого типа, в котором предварительно было сохранено процедурой MARK значение указателя кучи. Освобождается участок кучи от адреса, хранящегося в PTR, до конца кучи. Одновременно уничтожается список всех свободных фрагментов, которые, возможно, были созданы процедурами DISPOSE или FREEMEM.
 +
 +Функция SEG.
 +
 +Возвращает значение типа WORD, содержащее сегмент адреса указанного объекта. Вызов:
 +
 +SEG ( X )
 +
 +Здесь X – выражение любого типа или имя процедуры.
 +
 +Функция SIZEOF.
 +
 +Возвращает длину в байтах внутреннего представления указанного объекта. Вызов:
 +
 +SIZEOF ( X )
 +
 +Здесь X – имя переменной, функции или типа. Например, везде в программе из примера 6.1 вместо константы SIZEOFREAL можно было бы использовать обращение SIZEOF(REAL).