Demystifying pointers in Delphi by Lars Fosdal

  • Автор темы emailx45
  • 27
  • Обновлено
  • 14, May 2020
  • #1
Demystifying pointers in Delphi Lars Fosdal - 31/Oct/2019

Скрытая информация :: Авторизуйтесь для просмотра »
gives you the pointer memory address of the content of Variable


 
var
v: integer;
p: pointer;
begin
v := 5;
p := @v; // @ finds the address of v


  • p now contains the memory address of v
  • ^ = Look at that address
  • p^ = v = 5


The ^ also is used when declaring a type pointer – i.e. a type safe reference to the memory we are going to access. A typed pointer is just the same size as the basic pointer type, since it also is just a memory reference, but we now also know the size of what the pointer refers to.

Use of pointers in APIs
APIs come in many flavours, and one frequently used model for Windows APIs is that you allocate a structure and then pass the address of that structure to the API. The API call will then fill the structure with data which you can use.
 
type
TData = Array of char;
PData = ^TData; // Defines a typed pointer
TQueryNameData = record
Length: Integer;
Name: PData;
end;


So, what is the size of TQueryNameData?
 
SizeOf( TQueryNameData) = SizeOf(Length) + SizeOf(Name) = 4 + 4 = 8


Name: PData is only a pointer and hence needs to be allocated separately.

Hence in this example, we manage the memory ourselves
 
const
MaxLen = 100;
var
GetName: TQueryNameData;
begin
// first we prepare a memory location to hold the data
// This API allows us to define a max size that it will copy
// so that we can avoid a buffer overrun
GetName.Length := MaxLen;
GetName.Name := AllocMem(GetName.Length * SizeOf(char));


Then we pass the address of our variable to the API
 
if QueryNameAPI(@GetName)
then begin
// GetName.Name^ should now contain data filled from the API
// NB: Remember to call FreeMem for each AllocMem


Another API model is to have a Open/Process/Close use pattern, i.e. you first query the API to get a handle and f.x. a list of pointers.

These pointers will then be accessible until you call the related closing API.

In these cases, the memory is often managed by the API, and if you want to keep the values, you have to copy them during the process phase.


 

type

TWho = record

Name: PAnsiString;

Address: PAnsiString;

end;

TWhoList = Array of TWho;

PWhoList = ^TWhoList;

TQueryWhoListData = record

Count: Integer;

Who: PWhoList;

end;

var

Names: TQueryWhoListData;

begin

var h: HANDLE = AcquireWhoList(@Names);

//

if h > 0 then // The API gave us a list

begin

try

for var ix := 0 to Names.Count - 1 do

Writeln(Names.Who^[ix].Name^, ' ', Names.Who^[ix].Address^);

// or copy the data to your own structure

finally

ReleaseWhoList(h); // Let go of the API

end;

end;

end;




Look at Names.Who^[ix].Name^:

  • The first ^ points to the address where the array of TWho records is located.
  • The second ^ points to the address where the Name Ansistring is located.


The same goes for Names.Who^[ix].Address^ as well.

In modern Delphi you can actually do away with the ptr^ notation for typed pointers, and simply type ptr, and the compiler understands that it is what you refer to that you want, and not the address of what you refer to.

This means that for typed pointers, Names.Who^[ix].Address^ can be written as Names.Who[ix].Address instead.

Personally I sometimes like to use the old-school notation for clarity.

So, why can’t you keep referencing the data you received after letting go of the API?

The simple answer is that you no longer can trust the referred memory to be available and unchanged.

Typically, the API opening may allocate a block of memory that it fills and passes you, and it will release that block after you close the API.


emailx45


Рег
05 May, 2008

Тем
607

Постов
1273

Баллов
7343
Тем
49554
Комментарии
57426
Опыт
552966

Интересно