Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > указатели. И какой от них толк?


Автор: Teleport 13.12.2008, 14:57
вот читаю об указателях. Прочитал кое что и понял, как их объявлять - эти указатели. Также понял, что 
"Используя указатели мы можем обращаться к любой ячейке в памяти и использовать размещённые там данные."

Вопрос1: идентичны ли строки:
Код

ptrString : PString;

и
Код

ptrString : ^String;


или же нужно использовать вот так:
Код

type
ptrString : ^String;
var
 ptrString : PString;



Вопрос 2: Зачем нужны указатели - вот не пойму никак. Везде описано - как объявить и как с ними операции проводить. Но не могу найти примера - где без указателей не обойтись или с указателями проще решить задачу чем без них.  Прошу привести простые примеры с пояснением.

Автор: Rrader 13.12.2008, 16:06
Указатели - вещь незаменимая во многих случаях. Позволю себе привести некоторые:

1) Адресная арифметика - позволяет увеличивать или уменьшать значение указателя на размер его типа, то есть позволяет "путешествовать" по памяти. Конечно, можно и не на размер типа увеличивать адрес в указателе, если указатель приводить к нужному типу при арифметических операциях. 

Классический пример - вычисление длины строки PAnsiChar. Строка оканчивается символом #0, а все символы расположены линейно по порядку - для подсчета длины можно просто инкрементировать указатель на текущий символ.

Код

function StringLength(P: PAnsiChar): Cardinal;
begin
  Result := 0;
  if P = NIL then Exit;
  while P^ <> #0 do
  begin
    Inc(Result);
    Inc(P);
  end;
end;


2) Возможность произвольного доступа к данным, при этом код получается более наглядным.

Удобно при работе с API. Допустим, в функцию передается целая структура, но параметр числовой. Как обработать? Тут помогают указатели. Рассмотрим жутко напаренный пример, но подобные вещи в API сплошь и рядом: обработка разнообразных сообщений, hook-процедур и т.д.)

Код

type
  PMyRecord = ^TMyRecord;
  TMyRecord = packed record
    FStringField: ShortString;
    FIntField: Integer;
  end;

....

procedure MyProc(Param: Integer);
begin
  with PMyRecord(Param)^ do
  begin
    ShowMessage(FStringField);
    ShowMessage(IntToStr(FIntField));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Rec: TMyRecord;
begin
  Rec.FStringField := 'Text';
  Rec.FIntField := 10;
  MyProc(Integer(@Rec));
end;


В своей практике я очень часто использую адресную арифметику вместе с произвольным доступом, например, для путешествия по элементам таблиц Windows smile 

3) Еще одна интересная особенность - с указателями часто можно выиграть в быстродействии. Рассмотрим две функции, обе принимают большой статический массив и что-нибудь с ним делают.

Код

type
  PMyArray = ^TMyArray;
  TMyArray = array[1..10000] of Integer;
...
procedure Proc1(Value: PMyArray);
var
  I :Integer;
begin
  for I := 1 to High(Value^) do
    Value^[I] := 1;
end;

procedure Proc2(Value: TMyArray);
var
  I: Integer;
begin
  for I := 1 to High(Value) do
    Value[I] := 1;
end;


Замерим их примерное быстродействие через GetThreadTimes.

Код

procedure TForm1.Button2Click(Sender: TObject);

 function FileTimeToQuadWord(FileTime: TFileTime): Int64;

    function Int64ShllMod32(Value: Int64; ShiftCount: DWORD) :Int64;
    begin
      Result := Value shl ShiftCount;
    end;

  begin
    Result := Int64ShllMod32(FileTime.dwHighDateTime, 32)
      or FileTime.dwLowDateTime;
  end;

var
  ftKernelTimeStart, ftKernelTimeEnd: TFileTime;
  ftUserTimeStart, ftUserTimeEnd: TFileTime;
  ftDummy: TFileTime;
  qwKernelTimeElapsed1, qwUserTimeElapsed1, qwTotalTimeElapsed1: Int64;
  qwKernelTimeElapsed2, qwUserTimeElapsed2, qwTotalTimeElapsed2: Int64;
  I: Integer;
  Arr: TMyArray;
begin
  { Часть 1 }
  GetThreadTimes(GetCurrentThread, ftDummy, ftDummy,
    ftKernelTimeStart, ftUserTimeStart);

  // Начало кода
  for I := 0 to 100000 do
    { Принимает указатель }
    Proc1(@Arr);
  // Конец кода

  GetThreadTimes(GetCurrentThread, ftDummy, ftDummy,
    ftKernelTimeEnd, ftUserTimeEnd);
  qwKernelTimeElapsed1 := FileTimeToQuadWord(ftKernelTimeEnd) -
    FileTimeToQuadWord(ftKernelTimeStart);
  qwUserTimeElapsed1 := FileTimeToQuadWord(ftUserTimeEnd) -
    FileTimeToQuadWord(ftUserTimeStart);
  qwTotalTimeElapsed1 := qwKernelTimeElapsed1 + qwUserTimeElapsed1;
  { Часть 2 }
  GetThreadTimes(GetCurrentThread, ftDummy, ftDummy,
    ftKernelTimeStart, ftUserTimeStart);

  // Начало кода
  for I := 0 to 100000 do
    { Принимает массив }
    Proc2(Arr);
  // Конец кода

  GetThreadTimes(GetCurrentThread, ftDummy, ftDummy,
    ftKernelTimeEnd, ftUserTimeEnd);
  qwKernelTimeElapsed2 := FileTimeToQuadWord(ftKernelTimeEnd) -
    FileTimeToQuadWord(ftKernelTimeStart);
  qwUserTimeElapsed2 := FileTimeToQuadWord(ftUserTimeEnd) -
    FileTimeToQuadWord(ftUserTimeStart);
  qwTotalTimeElapsed2 := qwKernelTimeElapsed2 + qwUserTimeElapsed2;
  { Результат }
  ShowMessage(IntToStr(Round((qwTotalTimeElapsed2 - qwTotalTimeElapsed1) /
    qwTotalTimeElapsed2 * 100)) + '%');
end;


У меня разница в быстродействии примерно 40%, то есть функция, принимающая указатель, работает быстрее. Подумай сам, почему так происходит smile 

4) Добавлю еще один классический пример, где без указателей не обойтись smile 

Допустим, нужно создать односвязный список через массив записей (10 элементов). В каждой записи ровно два поля - указатель на следующий элемент списка и данные (пусть у нас это целое число). Односвязный список подразумевает, что мы можем проходить его только в одном направлении. Следующее объявление некорректно:
Код

type
  TOneWayListItem = record
    { Следующий элемент }
    FNextItem: TOneWayListItem;
    { Данные }
    FData: Integer;
  end;

  TOneWayList = array[1..10] of TOneWayListItem;

Цитата

[DCC Error] Unit1.pas(19): E2086 Type 'TOneWayListItem' is not yet completely defined

Решить проблему можно с помощью указателей. Дефинировать (объявлять) их можно до дефиниции самого типа. Вот рабочий вариант:
Код

type
  POneWayListItem = ^TOneWayListItem;
  TOneWayListItem = record
    { Следующий элемент }
    FNextItem: POneWayListItem;
    { Данные }
    FData: Integer;
  end;

  TOneWayList = array[1..10] of TOneWayListItem;

С таким списком нужно работать, самостоятельно выделяя и освобожая память.

Автор: Teleport 13.12.2008, 17:23
более менее понял только первые 2 примера.. smile спасибо smile

Автор: NewDima 15.12.2008, 05:45
так же не обойтись без указателей при создании графов. Закономерные структуры вообще хранятся в динамических структурах, а без указателей здесь не обойтись.
и, кстати,
Код

type
ptrString = ^string;

 smile 

Автор: Christoph 15.12.2008, 18:32
А что если напишу без type? то не будет работать?  smile 

Автор: NewDima 15.12.2008, 18:49
я про знак "="

Автор: Teleport 15.12.2008, 21:46
точно, знак равно smile Спасибо smile

Автор: ama_kid 16.12.2008, 15:19
Цитата(Teleport @  13.12.2008,  15:57 Найти цитируемый пост)
Но не могу найти примера - где без указателей не обойтись
попробуй без указателя сменить главную форму приложения Delphi в рантайме  smile 
Код
procedure TForm1.Button1Click(Sender: TObject);
var
 pMain:pointer;
begin
 pMain:[email protected];
 TForm(pMain^):=Form2;
end;


Автор: bems 16.12.2008, 21:36
ama_kid, грязный хак. А если в следующей версии для чтения этого свойства будет использоваться функция, а не поле? (имеют право, это внутренние дела класса)

Автор: ama_kid 16.12.2008, 23:32
Цитата(bems @  16.12.2008,  22:36 Найти цитируемый пост)
грязный хак
не спорю, но суть вопроса-ответа не в этом smile
Цитата(bems @  16.12.2008,  22:36 Найти цитируемый пост)
А если в следующей версии для чтения этого свойства будет использоваться функция, а не поле?
Имеют право, да, но в этом случае с тем же успехом можно воспользоваться ассемблером и знанием смещения адреса приватной переменной от начала класса... Но, напоминаю, вопрос-то был в другом...

Добавлено через 2 минуты и 24 секунды
я лишь привел пример, прямо иллюстрирующий полезность указателей в ответ на изначальный вопрос... 

Автор: Bose 17.12.2008, 03:26
Без указателей вообще никак. Любая переменная объектного типа по сути является указателем.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)