Заметки
Арифметика указателей

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика
Арифметика указателей - замечательная возможность, появившаяся в Delphi 2009 и контролируемая директивой $POINTERMATH, позволяющая работать с типизированными указателями как с неограниченными индексированными массивами (с помощью оператора array []), и даже напрямую обращаться к какому либо элементу такого массива по средствам простых арифметических операций непосредственно с переменной указателя.

Директива $POINTERMATH имеет локальную область действия и по умолчанию выключена ({$POINTERMATH OFF}). В любой момент она может быть включена для участка кода с помощью директивы {$POINTERMATH ON}, и выключена после него с помощью {$POINTERMATH OFF}. Если директиву не выключить, ее действие сохранится до конца модуля.

type
  TPointList = array[0..10] of TPoint;

procedure FillList(p: PPoint; l, h: Integer);
var
  i: Integer;
begin
   for i := l to h do
   begin
      {$POINTERMATH ON}
      p[i].X := Random(100);
      p[i].Y := Random(100);
      {$POINTERMATH OFF}
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pl: TPointList;
begin
   FillList(@pl, Low(pl), High(pl));
end;

В приведенном выше примере инициализируется массив точек. Обратите внимание на способ доступа к элементам массива в цикле, без директивы $POINTERMATH это было бы невозможно.

Директива также может применяться при объявлении типа указателя, при этом арифметика указателей будет доступна для этого типа в любом месте кода без предварительной обёртки в директивы $POINTERMATH.

type
  {$POINTERMATH ON}
  PPoint = ^TPoint;
  {$POINTERMATH OFF}
  TPointList = array[0..10] of TPoint;

procedure FillList(p: PPoint; l, h: Integer);
var
  i: Integer;
begin
   for i := l to h do
   begin
      p[i].X := Random(100);
      p[i].Y := Random(100);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pl: TPointList;
begin
   FillList(@pl, Low(pl), High(pl));
end;

Так же обратите свое внимание на то, что в приведенных выше примерах увеличение индекса массива типа эквивалентно увеличению указателя на размер этого типа. Такой подход избавляет нас он необходимости объявлять специальный тип PPointArray.

type
  PointArray  = array[0..$effffff] of TPoint;
  PPointArray = ^PointArray;

Также вместо p[i] можно использовать синтаксис (p + i)^, который приводит к тому же результату. Попробуем таким образом посмотреть значение какого-нибудь элемента в инициализированном массиве:

procedure ShowPointByIndex(p: PPoint; Index: Integer);
begin
   {$POINTERMATH ON}
   ShowMessage('X: ' + (p+Index)^.X.ToString +
               ' Y: ' + (p+Index)^.Y.ToString);
   {$POINTERMATH OFF}
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pl: TPointList;
begin
   FillList(@pl, Low(pl), High(pl));
   // Смотрим 6-ой элемент (массив индексируется с нуля)
   ShowPointByIndex(@pl, 5);
end;

Директива $POINTERMATH влияет только на типизированные указатели. Переменные типа Pointer не позволяют использовать математические функции указателя, так как указатель типа фактически указывает на элемент void размером 0 байт. Нетипизированные переменные или константы не затрагиваются, поскольку на самом деле они не являются указателями.

Такая вот арифметика!

При использовании материала - ссылка на сайт обязательна