150 - 05: Arrays and Records
time2 := Now;
Show (n2);
Show ('Variants: ' + FormatDateTime (
'ss.zzz', Time2-Time1) + ' seconds');
The timing code is worth looking at, because it's something you can easily adapt to
any kind of performance test. As you can see, the program uses the Now function to
get the current time and the FormatDateTime function to output the time difference,
showing only the seconds ("ss") and the milliseconds ("zzz").
In this example the speed difference is actually so great that you'll notice it even
without precise timing:
49999995000000
Variants: 01.169 seconds
49999995000000
Integers: 00.026 second
These are the numbers I get on my Windows virtual machine, and that's about 50
times slower for the variant based code. The actual values depend on the computer
you use to run this program, but the relative difference won't change much. Even on
my Android phone I get a similar proportion (but much longer times overall):
49999995000000
Variants: 07.717 seconds
49999995000000
Integers: 00.157 second
On my phone this code takes 6 times as much as on Windows, but now the fact is the
net different is over 7 seconds, making the variant based implementation noticeably
slow to the user, while the Int64 based one is still extremely fast (a user would
hardly notice a tenth of a second).
What About Pointers?
Another fundamental data type of the Object Pascal language is represented by
pointers. Some of the object-oriented languages have gone a long way to hide this
powerful, but dangerous, language construct, while Object Pascal lets a programmer
use it when needed (which is generally not very often).
But what is a pointer, and where does its name come from? Differently than most
other data types, a pointer doesn't hold an actual value, but it holds an indirect ref-
erence to a variable, which in turn has a value. A more technical way to express this
is that a pointer type defines a variable that holds the memory address of another
variable of a given data type (or of an undefined type).
Marco Cantù, Object Pascal Handbook
05: Arrays and Records - 151
note This is an advanced section of the book, added here because pointers are part of the Object Pascal
language and should be part of the core knowledge of any developer, although it is not a basic
topic and if you are new to the language you might want to skip this section the first time you read
the book. Again, there is a chance you might have used programming languages with no (explicit)
pointers, so this short section could be an interesting read!
The definition of a pointer type is not based on a specific keyword, but uses a special
symbol, the caret (^). For example you can define a pointer to variable of the Integer
type with the following declaration:
type
TPointerToInt = ^Integer;
Once you have defined a pointer variable, you can assign to it the address of another
variable of the same type, using the @ operator:
var
P: ^Integer;
X: Integer;
begin
X := 10;
P := @X;
// change the value of X using the pointer
P^ := 20;
Show ('X: ' + X.ToString);
Show ('P^: ' + P^.ToString);
Show ('P: ' + Integer(P).ToHexString (8));
This code is part of the PointersTest application project. Given the pointer P refers
to the variable X, you can use P^ to refer to the value of the variable, and read or
change it. You can also display the value of the pointer itself, that is the memory
address of X, by casting the pointer to an integer. Rather than showing the plain
numeric value, the code shows the hexadecimal representation, which is more com-
mon for memory addresses. This is the output (where the pointer address might
depend on the specific compilation):
X: 20
P^: 20
P: 0018FC18
warn Casting the pointer to an Integer is correct code only on 32-bit platforms, not on 64-bit ones. A
better option is to cast to NativeInt, however that type lacks the integer helpers, and that would
have made the sample code more complex. So the code of this demo is 32-bit specific.
Let me summarize, for clarity. When you have a pointer P:
• By using the pointer directly (with the expression P) you refer to the address of
the memory location the pointer is referring to
Marco Cantù, Object Pascal Handbook
152 - 05: Arrays and Records
• By dereferencing the pointer (with the expression P^) you refer to the actual con-
tent of that memory location
Instead of referring to an existing memory location, a pointer can also refer to a new
and specific memory block dynamically allocated on the heap with the New proce-
dure. In this case, when you don't need the value accessed by the pointer anymore,
you’ll also have to get rid of the memory you’ve dynamically allocated, by calling the
Dispose procedure.
note Memory management in general and the way the heap works in particular are covered in Chapter
13. In short, the heap is a (large) area of memory in which you can allocate and release blocks of
memory in no given order. As an alternative to New and Dispose you can use GetMem and FreeMem,
but the former two are preferable and safer to use.
Here is a code snippet that allocates memory dynamically:
var
P: ^Integer;
begin
// initialization
New (P);
// operations
P^ := 20;
Show (P^.ToString);
// termination
Dispose (P);
If you don't dispose of the memory after using it, your program may eventually use
up all the available memory and crash. The failure to release memory you don't need
any more is known as a memory leak.
note To be safer the code above should indeed use an exception handling try-finally block, a topic I
decided not to introduce at this point of the book, but I'll cover later in Chapter 9.
If a pointer has no value, you can assign the nil value to it. You can test whether a
pointer is nil to see if it currently refers to a value with a direct equality test or by
using the specific Assigned function as shown below.
This kind of test is often used, because dereferencing (that is accessing the value at
the memory address stored in the pointer) an invalid pointer causes a memory
access violation (with slightly different effects depending on the operating system):
var
P: ^Integer;
begin
P := nil;
Show (P^.ToString);
Marco Cantù, Object Pascal Handbook
05: Arrays and Records - 153
You can see an example of the effect of this code by running the PointersTest appli-
cation project. The error you'll see (on Windows) should be similar to:
Access violation at address 0080B14E in module 'PointersTest.exe'.
Read of address 00000000.
One of the ways to make pointer data access safer, is to add a “pointer is not null”
safe-check like the following:
if P <> nil then
Show (P^.ToString);
As I mentioned earlier, an alternative way, which is generally preferable for read-
ability reasons, is to use the Assigned pseudo-function:
if Assigned (P) then
writeln (P^.ToString);
note Assigned is not a real function, because it is “resolved” by the compiler producing the proper
code. Also, it can be used over a procedural type variable (or method reference) without actually
invoking it, but only checking if it is assigned.
Object Pascal also defines a Pointer data type, which indicates untyped pointers
(such as void* in the C language). If you use an untyped pointer you should use Get-
Mem instead of New (indicating the number of bytes to allocate, given this value
cannot be inferred from the type itself). The GetMem procedure is required each time
the size of the memory variable to allocate is not defined.
The fact that pointers are seldom necessary in Object Pascal is an interesting advan-
tage of this language. Still, having this feature available can help in implementing
some extremely efficient low level functions and when calling the API of an operat-
ing system. In any case, understanding pointers is important for advanced
programming and for a full understanding of language object model, which uses
pointers (generally called references) behind the scenes.
File Types, Anyone?
The last Object Pascal data type constructor covered (briefly) in this chapter is the
file type. File types represent physical disk files, certainly a peculiarity of the original
Pascal language, given very few old or modern programming languages include the
notion of a file as a primitive data type. The Object Pascal language has a file key-
word, which is a type specifier, like array or record. You use file to define a new
type, and then you can use the new data type to declare new variables:
Marco Cantù, Object Pascal Handbook