Язык Delphi

Переменные - указатели




Уроки Delphi
  1.  Первая программа
  2.  Использование компонентов
  3.  События Delphi
  4.  Типы данных Delphi
  5.  Создание своих типов данных
  6.  Выражения и операторы
  7.  Работа с файлами в Delphi
  8.  Дополнительные формы
  9.  Подпрограммы в Delphi
  10. Исключительные ситуации
  11. Взаимодействие приложения с пользователем
  12. Указатели в Delphi
  13. Обзор компонентов
  14. Работа со строками
  15. Создание интерфейса
  16. Графика в Delphi
  17. Многопоточность в Delphi
  18. Динамическое создание
        компонентов
Поиск по сайту




 Это важно:
   Метод Application.ProcessMessages;

 Это полезно:
   Параметр Sender в обработчиках событий;










Бояться не надо



   Как следует из названия, переменные - указатели это особый тип переменных, которые не содержат значения, а указывают на них - на ту ячейку памяти, где они фактически располагаются. И хотя справедливо считается, что использование указателей может приводить к трудно контролируемым ошибкам в программе, всё же переменные - указатели это очень эффективный инструмент для управления объектами в оперативной памяти компьютера.

   Конечно, ячейка памяти - это структура размером в один байт. Объекты же, с которыми работает программа, в основном намного большего размера. Соответственно, указатель содержит в себе адрес только первого байта той области оперативной памяти компьютера, где располагается данный объект. Зная тип и соответственно размер объекта, можно прочитать его целиком.


Описание переменных - указателей

   Указатель описывается ключевым словом Pointer. По первой букве ключевого слова принято называть переменные - указатели с первой буквы P:

  var PIndexer: Pointer;//нетипизированный указатель

   Ключевым словом Pointer задаётся так называемый нетипизированный указатель, по аналогии с нетипизированным файлом. Нетипизированный указатель содержит просто адреc некой ячейки памяти. Объект, располагающийся начиная с этой ячейки, соответственно может быть совершенно любого типа и размера.

   Также в Delphi существуют и типизированные указатели. Они могут указывать на объект соответствующего типа и размера. Именно "могут указывать", потому что это по прежнему адрес одной - первой ячейки области памяти, где располагается объект. И далее его использование в программе зависит от программиста!
   Итак, типизированный указатель описывается ключевым словом означающим данный тип, перед которым ставится значок ^:

  var PInteger: ^Integer;//указатель на переменную целого типа
         PText: ^String;//указатель на переменную типа String


   Также можно описать любой свой тип, и задать переменную-указатель данного типа:

type TMyType = Record
   X: Integer;
   S: String;
end;

var PMyPointer: ^TMyType;


   Естественно, можно определить тип, для описания через него переменных - указателей. Делается это в том числе и потому, что, например, в процедурах и функциях в качестве параметров можно использовать только заранее описанный тип данных. Например, следующее описание задаёт функцию с параметром, являющимся указателем ранее описанного типа, результат которой также является указателем данного типа:

type TPMyPointer = ^TMyType;

function MyFunc(Point: TMyPointer): TMyPointer;


   Ну вот, надеюсь теперь вы не боитесь разнообразных вариантов описания таких переменных как указатели.


Использование переменных - указателей

   Использование указателей предполагает:
  1. присвоение значения указателю;
  2. изменение значения указателя;
  3. создание области памяти нужного типа и присвоение его адреса указателю;
  4. запись значения в область памяти, адресуемой указателем, и чтение из неё;
  5. освобождение области памяти, адресуемой данным указателем...
   Описанный указатель без присвоенного значения указывает на совершенно неопределённую ячейку памяти. Попытка использовать такой указатель чревата крахом программы. Поэтому всем указателям нужно явно присваивать значения.

   1. Указателю можно присвоить значение другого указателя. В результате оба указателя будут указывать на одну и ту же ячейку памяти. Также указателю можно присвоить пустое значение с помощью ключевого слова nil:

var P1, P2: Pointer;
begin
  P1:=P2;//Присвоение указателю значения другого указателя
  P2:=nil;//Присвоение указателю "пустого" значения
end;

   Указатель со значением nil не адресует никакой ячейки памяти и единственное, что с ним можно сделать - это сравнить с другим указателем или со значением nil.


   2. Значение типизированного указателя можно увеличить или уменьшить на размер области памяти, занимаемой объектом данного типа. Для этого служат операции инкремента и декремента:

type P: ^Integer;
begin
  inc(P);//увеличение значения указателя на 4 байта (размер типа Integer)
  dec(P);//уменьшение значения указателя на 4 байта (размер типа Integer)
end;

   Попытка выполнить операциии inc либо dec с нетипизированным указателем вызовет ошибку на этапе компиляции, так как компилятору неизвестно, насколько именно изменять значение указателя.


   3. Процедурой New можно создать область памяти сответствующего типа и присвоить её адрес указателю (инициировать указатель):

var PInt: ^Integer;
begin
  New(PInt);//Указатель PInt получает значение адреса созданной области памяти типа Integer
end;

   Поскольку с областью памяти, созданной с помощью процедуры New, не связана ни одна переменная, но там содержится реальное используемое значение, то можно считать, что это значение связано с некой "безымянной переменной". Обращаться к ней по имени переменной невозможно, а можно оперировать только используя указатель.

   Также задать указателю адрес объекта можно с помощью операции, называемой "взятие адреса", которая обозначается значком @. При этом создавать область памяти уже не нужно, так как она создана предварительным описанием данного объекта:

var MyVar: TMyType;//Описание переменной, при этом выделяется область памяти соответствующего размера
    P: ^TMyType;//Задаётся указатель соответствующего типа
begin
  P:=@MyVar;//Указатель получает адрес области памяти, занимаемой переменной MyVar
end;


   4. Если область памяти уже создана и её адрес присвоен указателю, то в ячейку памяти, адресуемую данным указателем, можно записать значение объекта, соответствующего типу указателя. Для этого служит операция, обозначаемая также значком ^, стоящим после имени указателя, например: P^. Эта операция называется "разыменование указателя". Также с помощью этой операции со значением в данной ячейке памяти можно делать всё что нужно:

var MyVar: Integer;
    P: ^Integer;
begin
  P:=@MyVar;//Указатель получает адрес области памяти, занимаемой переменной MyVar
  P^:=2;//В ячейку памяти по адресу переменной MyVar записывается значение 2
  Form1.Caption:=IntToStr(P^+3);//В заголовке Формы появится число 5
end;


   С обычными переменными всё просто, но возникает вопрос, как получить значение по адресу указателя, если тип переменной - запись с несколькими полями? Аналогично:

type TMyRec = Record
    N: Integer;
    S: String;
end;

var MyRec: TMyRec;
    PRec: ^TMyRec;

begin
  PRec:=@MyRec;//Указатель получает адрес области памяти, занимаемой переменной MyRec
  PRec^.S:='Строка данных';//С помощью указателя производится изменение строкового поля записи
  PRec^.N:=256;//С помощью указателя производится изменение числового поля записи
end;

   А теперь уберите стрелку возле PRec: PRec.S:='Строка данных'; Вы увидите, что никакой ошибки ни компилятор, ни выполнение программы не показали! Выходит, выражения PRec^.S и PRec.S аналогичны.

   Далее, а как получить значение, если указатель это элемент массива, например:

var PArray: Array[1..100] of ^Integer;
    X: Integer;


   Ни PArray^[10], ни PArray[10]^ не являются правильными выражениями. Ну конечно, нужно использовать скобки:

X:=(PArray[10])^;

   5. Память, выделенную процедурой New, всегда нужно явно освобождать. Освободить область памяти, адресуемую указателем, инициированным с помощью New, можно процедурой Dispose:

var MyVar: TMyType;
    P: ^TMyType;
begin
  P:=@MyVar;
  Dispose(P);//Освобождение области памяти, адресуемой указателем P
end;

   При выполнении процедуры Dispose указатель снова приобретает неопределённое значение, не равное даже nil, и его использование может привести к неопределённым результатам, даже к краху программы.


Применение переменных - указателей

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


Взаимодействие приложения с пользователем           В начало урока          Обзор компонентов Delphi 

Уроки Delphi начинающим







© 2023 Delphi-Manual.ru - Уроки Delphi начинающим с нуля