Lecture 20: Pointers |
A pointer is a special type of variable. In itself it contains no useful information. It is only an address to what (might) contain useful information. |
A pointer contains the address of a place in memory |
This is like keeping the address of an appartment
in my addressbook. It is only an address and nothing more.
To declare a pointer we can use the following
syntax:
Var p: pointer;
@x or Addr(x) returns a pointer to variable x |
|
In
the example on the left, x is a variable of type real that has
a value of 3.0. When we want the pointer p to point to this variable,
we can use
p := @x; or p := Addr(x); The value of p is now a memory address, namely the address that contains the variable x. To show the contents of the memory of what p points to, we might think that we can do this with WriteLn(p^); There is only one thing wrong with this. The compiler only knows that p is of type pointer. When we run the program we let it point to a certain place in memory, but the program doesn't know what (type of information) the pointer is pointing to. Picture the following situation. A pointer p points to a place in memory. If it is pointing to a byte, the value is different than if it is pointing to an integere or a word. In the example below, p^ pointing to a byte is 129 (binary: 10000001), while the same p^ pointing to the same place in memory, but pointing to a word is equal to 25473 (binary: 0110001110000001), or p^ pointing to a longint (4 bytes, 32 bits) will give 743924609 (binary: 00101100010101110110001110000001). (Note that in Intel-processor-based computers numbers are stored with their lowest value bit [LSB=least significant bit] first). Therefore, we have to specify the type the pointer is pointing at. For that we have to go back to the declaration of the pointer. Instead of just using 'pointer' for the type, we will specify what kind of variable the pointer will point to: |
Var p: pointer; |
|
The first form declares a general pointer, without specifying what type
of data it will point to.
The second form also specifies the type of data it points to. For type
we can use any type we have learned. From the simple variables (integer,
real, etc) to the complicated types (record, array), to combinations of
these (arrays of records, records of arrays).
Examples:
Var byteptr: ^byte;
wordPtr:
^word;
real6arrayptr:
^array[1..6] of real;
Now let's see an example to show check if we have everything under control. We are going to create a pointer of type 'pointing to a word', and let it point to a word:
Var wordptr: ^word;
w: word;
begin
(* assign a value
to the word *)
w := 25473;
(* let a 'pointer
to word' point to our word *)
wordptr := Addr(w);
(* show the contents
of the memory wordptr points to *)
WriteLn(wordptr^);
end.
output:
25473
Now let's see a more complicated example. We are going to create a pointer of type 'pointing to a byte', and let it point to our word:
Var byteptr: ^byte;
w: word;
begin
(* assign a value
to the word *)
w := 25473;
(* let a 'pointer
to word' point to our word *)
byteptr := Addr(w);
(* show the contents
of the memory wordptr points to *)
WriteLn(byteptr^);
end.
output:
129
This shows that we have to be careful what our pointer points to. The
value depends on the type!
Why use pointers? There are several reasons
to use pointers instead of normal variables. The most important ones are
|
|
Speed: Imagine you write
a procedure that has an array as parameter. Every time the program calls
the procedure, the entire array has to be copied in memory and the procedure
executed. If, instead of telling the procedure the value of every element
of the array, it would be much faster to only tell the address of the array.
This means copying only a single variable (a pointer is just 4 bytes in
Intel computers).
PROGRAM TestSpeed;
Type ra = array[1..1000] of real;
PROCEDURE SlowProc(a: ra);
begin
|
PROGRAM TestSpeed;
(* define an array and a pointer to
PROCEDURE FastProc(a: rap);
begin
|
execution time on a Pentium II 450 MHz: 115 s. |
execution time on a Pentium II 450 MHz: 1 s. |
(In fact, the program on the right does the same as a program that passes the array by reference (see lecture 15). PROCEDURE SlowProc(Var a: ra); is also fast. Passing by reference means that a pointer is passed to the procedure.)
Flexibility: If, at the beginning
of the program we do not know yet how many variables we need we would have
to reserve space for all possible eventualities. If we want to write a
program that calculates the first N prime numbers, with N given by the
user, we would have to declare an array of maximum size to be sure that
we can fit the users request in it. Something like this:
Var prime: array[1..10000000] of longint;
With this we would completely occupy the memory
of the computer. Nothing else can run anymore. Much nicer would be if we
could declare the array (the variables) dynamically so that we only use
memory if we really need it. With pointers this is easily possible.
The pointers and the idea of dynamic creation
of variables also lies at the basis of object-oriented programming, which
is the type of programming of every modern computer language. Object oriented
programming is outside the scope of this lecture, though.