Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Pointers in Delphi

I have been developing for some time now, and I have not used pointers in my development so far.

So what are the benefits of pointers? Does an application run faster or uses fewer resources?

Because I am sure that pointers are important, can you “point” me to some articles, basic but good to start using pointers in Delphi? Google gives me too many, too special results.

like image 993
Jlouro Avatar asked Feb 27 '09 15:02

Jlouro


3 Answers

A pointer is a variable that points to a piece of memory. The advantages are:

  • you can give that piece of memory the size you want.
  • you only have to change a pointer to point to a different piece of memory which saves a lot of time copying.

Delphi uses a lot of hidden pointers. For example, if you are using:

var
  myClass : TMyClass;
begin
  myClass := TMyClass.Create;

myClass is a pointer to the object.

An other example is the dynamic array. This is also a pointer.

To understand more about pointers, you need to understand more about memory. Each piece of data can exist in different pieces of data.

For example global variables:

unit X;

interface

var
  MyVar: Integer;

A global variable is defined in the datasegment. The datasegment is fixed. And during the lifetime of the program these variables are available. Which means the memory can not be used for other uses.

Local variables:

procedure Test;
var
  MyVar: Integer;

A local variable exists on the stack. This is a piece of memory that is used for housekeeping. It contains the parameters for the function (ok some are put in a register but that is not important now). It contains the return adress so the cpu knows where to return if the program has ended. And it contains the local variables used in the functions. Local variables only exists during the lifetime of a function. If the function is ended, you can't access the local variable in a reliable way.

Heap variables:

procedure Test2;
var
  MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;

The variable MyClass is a pointer (which is a local variable that is defined on the stack). By constructing an object you allocate a piece of memory on the heap (the large piece of 'other' memory that is not used for programs and stacks). The variable MyClass contains the address of this piece of memory. Heap variables exist until you release them. That means that if you exit the funcion Test2 without freeing the object, the object still exists on the heap. But you won't be able to access it because the address (variable MyClass) is gone.

Best practices

It is almost always preferably to allocate and deallocate a pointer variable at the same level.

For example:

var
  myClass: TMyClass;
begin
  myClass := TMyClass.Create;
  try
    DoSomething(myClass);
    DoSomeOtherthing(myClass);
  finally
    myClass.Free;
  end;
end;

If you can, try to avoid functions that return an instance of an object. It is never certain if the caller needs to dispose of the object. And this creates memory leaks or crashes.

like image 93
Toon Krijthe Avatar answered Oct 05 '22 11:10

Toon Krijthe


You have been given a lot of good answers so far, but starting with the answer that you are already dealing with pointers when you use long strings, dynamic arrays and object references you should start to wonder why you would use pointers, instead of long strings, dynamic arrays and object references. Is there any reason to still use pointers, given that Delphi does a good job hiding them from you, in many cases?

Let me give you two examples of pointer use in Delphi. You will see that this is probably not at all relevant for you if you mostly write business apps. It can however become important if you ever need to use Windows or third party API functions that are not imported by any of the standard Delphi units, and for which no import units in (for example) the JEDI libraries can be found. And it may be the key to achieve that necessary last bit of speed in string-processing code.

Pointers can be used to deal with data types of varying sizes (unknown at compile time)

Consider the Windows bitmap data type. Each image can have different width and height, and there are different formats ranging from black and white (1 bit per pixel) over 2^4, 2^8, 2^16, 2^24 or even 2^32 gray values or colours. That means that it is unknown at compile time how much memory a bitmap will occupy.

In windows.pas there is the TBitmapInfo type:

type
  PBitmapInfo = ^TBitmapInfo;
  tagBITMAPINFO = packed record
    bmiHeader: TBitmapInfoHeader;
    bmiColors: array[0..0] of TRGBQuad;
  end;
  TBitmapInfo = tagBITMAPINFO;

The TRGBQuad element describes a single pixel, but the bitmap does of course contain more than one pixel. Therefore one would never use a local variable of type TBitmapInfo, but always a pointer to it:

var
  BmpInfo: PBitmapInfo;
begin
  // some other code determines width and height...
  ...
  BmpInfo := AllocMem(SizeOf(TBitmapInfoHeader)
    + BmpWidth * BmpHeight * SizeOf(TRGBQuad));
  ...
end;

Now using the pointer you can access all pixels, even though TBitmapInfo does only have a single one. Note that for such code you have to disable range checking.

Stuff like that can of course also be handled with the TMemoryStream class, which is basically a friendly wrapper around a pointer to a block of memory.

And of course it is much easier to simply create a TBitmap and assign its width, height and pixel format. To state it again, the Delphi VCL does eliminate most cases where pointers would otherwise be necessary.

Pointers to characters can be used to speed up string operations

This is, like most micro optimizations, something to be used only in extreme cases, after you have profiled and found the code using strings to consume much time.

A nice property of strings is that they are reference-counted. Copying them does not copy the memory they occupy, it only increases the reference count instead. Only when the code tries to modify a string which has a reference count greater than 1 will the memory be copied, to create a string with a reference count of 1, which can then safely be modified.

A not-so-nice property of strings is that they are reference-counted. Every operation that could possibly modify the string has to make sure that the reference count is 1, because otherwise modifications to the string would be dangerous. Replacing a character in a string is such a modification. To make sure that the reference count is 1 a call to UniqueString() is added by the compiler whenever a character in a string is written to. Now writing n characters of a string in a loop will cause UniqueString() to be called n times, even though after the first time is is assured that the reference count is 1. This means basically n - 1 calls of UniqueString() are performed unnecessarily.

Using a pointer to the characters is a common way to speed up string operations that involve loops. Imagine you want (for display purposes) to replace all spaces in a string with a small dot. Use the CPU view of the debugger and compare the code executed for this code

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  i: integer;
begin
  Result := AValue;
  for i := 1 to Length(Result) do begin
    if Result[i] = ' ' then
      Result[i] := $B7;
  end;
end;

with this code

procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
  P: PAnsiChar;
begin
  Result := AValue;
  P := PAnsiChar(Result);
  while P[0] <> #0 do begin
    if P[0] = ' ' then
      P[0] := $B7;
    Inc(P);
  end;
end;

In the second function there will be only one call to UniqueString(), when the address of the first string character is assigned to the char pointer.

like image 43
mghie Avatar answered Oct 05 '22 11:10

mghie


You probably have used pointers, but you just don't know it. A class variable is a pointer, a string is a pointer, a dynamic array is a pointer, Delphi just hides it for you. You will see them when you are performing API calls (casting strings to PChar), but even then Delphi can hide a lot.

See Gamecats answer for advantages of pointers.

In this About.com article you can find a basic explanation of pointers in Delphi.

like image 42
The_Fox Avatar answered Oct 05 '22 13:10

The_Fox