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