Смекни!
smekni.com

Описание указателей (стр. 2 из 2)

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

SEG(X) - возвращает сегментную часть адреса;

OFS(X) - возвращает смещение.

Аргументом Х при обращении к этим функциям может служить любая переменная, в том числе и та, на которую указывает указатель. Например, еслиимеем

var

р: ^real;

begin

new(p);

p^ := 3.14;

end

то функция SEG(P) вернет сегментную часть адреса, по которому располагается 4-байтный указатель Р, в то время как SEG(P^) - сегмент 6-байтного участка кучи, в котором хранится число 3.14.

С другой стороны, с помощью встроенной функции PTR(SEG,OFS: WORD): POINTER можно создать значение указателя, совместимое с указателями любого типа. Таким образом, возможна такая последовательность действий. Вначале процедурой GETMEM из кучи забираются несколько фрагментов подходящей длины (напомню, что за одно обращение к процедуре можно зарезервировать не более 65521 байт динамической памяти). Для рас сматриваемого примера удобно резервировать фрагменты такой длины чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е.

200 * 10 = 2000 байт.

Начало каждого фрагмента, т.е. фактически начало размещения в памяти каждой строки, запоминается в массиве PTRSTR, состоящем из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель:

var

i, j: integer;

PtrStr: array [1..100] of pointer;

pr: ^real;

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 pr^ > 1 then

end

Поскольку оператор вычисления адреса PR := PTR... будет, судя по всему, использоваться в программе неоднократно, полезно ввести вспомогательную функцию GETR, возвращающую значение элемента матрицы, и процедуру PUTR, устанавливающую новое значение элемента. Каждая из них, в свою очередь, обращается к функции ADDRR для вычисления адреса. Ниже приводится программа, создающая в памяти матрицу из NxM случайных чисел и вычисляющая их среднее значение.

program Primer1;

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 * M): 12:10)

end. {Main}

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