Pointer variables are a special type of variables that do not store values but just point to them – to the memory cell where these values are kept. And though there is a quite logical opinion that the usage of pointers can result in different errors in a program, pointer variables can be a very efficient tool for managing objects in the RAM of a computer.
A memory cell is a one-byte structure. As for objects that a program works with, they are usually significantly bigger. Consequently, a pointer keeps an address of only the first byte of the RAM sector where a particular object is stored. When you know the type and size of an object, you can read it entirely.
Description of the pointer variables
A pointer is specified with a keyword Pointer. As a rule, pointer variables are specified with the first letter of this name – P:
The keyword Pointer is used for a so-called untyped pointer like in the case with an untyped file. An untyped pointer contains just an address of some memory cell. The object that is located starting with this cell can be of absolutely any type and size.
In Delphi, there are also typed pointers. They can point to an object of the relevant type and size. They can be named “point” as they still contain just an address of the first cell in the memory area where the object is located. And then its use in the program depends on the developer.
A typed pointer is specified with a keyword that defines a particular type and has a character ^:
12 | var PInteger: ^Integer;// pointer to a variable of the integer type
PText: ^String;//pointer to a variable of the String type |
It’s also possible to specify any type and assign a variable of this type:
It is possible to identify a type to use for describing pointer variables. It can be done for different reasons. For example, in procedures and functions, you can use only earlier described data types as parameters. For instance, the following description specifies the function with a parameter that is a pointer of the previously described type. The result of this function is also a pointer of this type:
The use of pointer variables
The use of pointer variables presupposes:
- Pointer value assignment
- Pointer value change
- Creation of a memory area of the necessary type and assignment of its address to the pointer
- Recording of the value to the memory area addressed by the pointer and reading data
- This memory area freeing
The described pointer without an assigned value points to an absolutely indefinite memory cell. Any efforts to use such a pointer may result in a program collapse. That’s why it is crucial to assign values to all pointers.
1. A pointer can have the value of another pointer. As a result, both pointers will point to the same memory cell. You can also assign a null-value to a pointer using the keyword nil:
12345 | var P1, P2: Pointer;
begin
P1:=P2;//Assignment of the value of another pointer
P2:=nil;//Assignment of a null-value
end; |
The pointer with a nil value doesn’t point to any memory cell and the only thing that you can do with it is to compare it with another pointer with the nil value.
2.The value of the typed pointer can be increased or decreased by the size of the memory area that is occupied by the object of this type. For this aim, we should use the increment and decrement operations:
12345 | type P: ^Integer;
begin
inc(P);// increase of pointers’ value by 4 bytes (size of Integer type)
dec(P);// decrease of pointers’ value by 4 bytes (size of Integer type)
end; |
A trial to execute the inc or dec operations with an untyped pointer will lead to an error at the compilation stage as the compiler doesn’t have any information on how the pointer’s value should be changed.
3.With the procedure New, it is possible to create a memory area of the relevant type and assign its address to a pointer (to initiate a pointer):
1234 | var PInt: ^Integer;
begin
New(PInt);// Pointer PInt gets the address of the created memory area of the type Integer
end; |
As the memory area, which was created with the procedure New, is not linked to any variable but it contains a real value that is being used, you can consider that a value is linked to an unnamed variable. It is impossible to call it by name but it is possible to operate it using a pointer.
It is also possible to assign an object address to a pointer using an operation that is called “address taking” and is identified with the sign @. And here you do not need to create a memory area as it is already created with a preliminary description of this object:
12345 | var MyVar: TMyType;//Description of the variable, memory area of the relevant size is assigned
P: ^TMyType;//A pointer of the relevant type is assigned
begin
P:=@MyVar;//A pointer gets the address of the memory area that is occupied by the MyVar variable
end; |
4. If a memory area is already created and its address is assigned to a pointer, you can add the value of the object that corresponds to the pointer type into the memory cell addressed by this pointer. For this aim, there is an operation that is also identified with a character ^ that should be placed after the pointer’s name, for example, P^. This operation is called “pointer unnaming”. Moreover, with the help of this operation, you can do whatever is required with the value in this memory cell:
1234567 | var MyVar: Integer;
P: ^Integer;
begin
P:=@MyVar;//A pointer gets an address of the memory area that is occupied by MyVar
P^:=2;//The value 2 is added to the memory cell located at the address of the variable MyVar
Form1.Caption:=IntToStr(P^+3);//The number 5 will appear in the heading of the Form
end; |
With common variables, everything is quite simple, but there is a question on how to get a value using a pointer’s address if a type of this variable is a record with several fields? Everything looks quite similar:
12345678910111213 | type TMyRec = Record
N: Integer;
S: String;
end;
var MyRec: TMyRec;
PRec: ^TMyRec;
begin
PRec:=@MyRec;//MyRec A pointer gets an address of the memory area that is occupied by MyRec
PRec^.S:='Data line';//The pointer is used to introduce changes in the string field
PRec^.N:=256;//The pointer is used to introduce changes in the number field
end; |
Now you need to move an arrow from PRec: PRec.S:=’Data line’; and you will see that neither the compilator nor the program has shown any errors. It means that expressions PRec^.S and PRec.S are identical.
Now, let’s see how to get a value if a pointer is an element of the array, for example:
12345678 | var PArray: Array[1..100] of ^Integer;
X: Integer;
PArray^[10] and PArray[10]^ are not correct expressions. It is necessary to use brackets:
X:=(PArray[10])^; |
5. The memory that is dedicated within the procedure New should be freed. To free the memory area addressed by the pointer that was initiated with the help of New, you can use the procedure Dispose:
123456 | var MyVar: TMyType;
P: ^TMyType;
begin
P:=@MyVar;
Dispose(P);// Freeing of memory area addressed by pointer P
end; |
During the Dispose procedure, the pointer again gets an indefinite value that even doesn’t equal nil and its usage can lead to indefinite results, including program collapse.
If you need to perform Delphi integrations, contact us for a consultation.