Использование указателей
Для рассматриваемого примера удобно резервировать фрагменты такой длины, чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е. 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 случайных чисел и вычисляющая их среднее значение.
Пример 6.1.
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 байт) может возникнуть переполнение при вычислении смещения последнего байта.