Beauty and danger of pointers is often evident with parameters pass to procedures or functions.
Let's begin getting familiar with them through real code.
type mypinteger = ^integer; // int* x in c++
Here we define a pointer to integer, so that every variable can be declared directly with mypinteger.
[Comment shows syntax used in C++ language]
Now the procedure we'll use:
procedure SquareByValue(x: integer); procedure SquareByReference(var x: integer); // int& x in c++ procedure SquareByPointer(x: pinteger); // int* x in c++
We want to make the square of integer number passed to all three them, the difference lies just of way we'll pass it, which will condition the code execution.
[Note that here we have predeclarations of procedures, not official ones]
First way, the common one: what happens here?
When SquareByValue(my_integer) is called, my_integer's value is passed to SquareByValue local variable x.
This one exists only in the procedure's context, that is it comes to lifeĀ and dies with procedure itself.
So we have two variables, my_integer and x, with the same value inside: by modifying x we leave my_integer untouched.
Second and third procedures are related to pointers; so pay attention.
SquareByReference has a particularity in declaration: the var access modifier before the x variable.
Differently from before, x now is another but valid label to address the same memory location pointed by the variable; it's like in this scheme: my_integer = x = 12.
x is again a local variable, whose life cycle is limited to procedure execution: but any change to it affects the variable passed.
SquareByPointer works instead with pointers, the memory location addresses.
By calling it we pass a pointer, that is a variable containing an address.
Its management is possible at two levels:
- arithmetic of pointers: adding integers to x we just make it point to a memory location (dangerous and used in very specific cases, not normally);
- value level, so that we have to indicate a way to express that we want the stuff pointed by x, and not its value which an address.
We are interested in second case:
procedure Tform1.SquareByPointer(x: pinteger); // int* x, in c++ begin x^ := x^ * x^; //dereference (*x, in c++) Memo.Lines.Add('Local variable x using by pointer =' + inttostr(x^)); end;
x^ means the (integer) value inside the memory location pointed by x.
With ^ caret sign we operate the so called Dereference, because with a pointer we have a reference, with caret we explicate the pointed value.
The main consequence of dereference is the same as ByReference mode: an original value is directly modified.