Различия

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

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

Предыдущая версия справа и слева Предыдущая версия
pascal:pointers [21/11/2009 18:12]
Олег Альбертович Скворцов
pascal:pointers [21/11/2009 18:16] (текущий)
Олег Альбертович Скворцов
Строка 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 байт) может возникнуть переполнение при вычислении смещения последнего байта.
  
 ===== Процедуры и функции для работы с динамической памятью ===== ===== Процедуры и функции для работы с динамической памятью =====
CC Attribution-Noncommercial 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0