[Back to TUTOR SWAG index]  [Back to Main SWAG index]  [Original]

                        Turbo Pascal for DOS Tutorial
                            by Glenn Grotzinger
                     Part 17: Primary Concepts of Pointers
                   copyright(c) 1995-96 by Glenn Grotzinger

Hello.  Here is a solution from last time...

program part16;

  var
    a: array[1..1000] of integer;
    number: integer;
    c, i, j, PIVOT, t: integer;
    found: boolean;
    location: integer;
    outfile: text;

  procedure quicksort(L, R: integer);
    { nothing to say we couldn't sort the data, especially with the
      fact that we will be performing 500 search hits on this 1000
      unit array...the overhead of sorting the data will be of good
      benefit in the long run... }

    begin
      if L < R then
        begin
          i := L + 1;
          j := R;
          PIVOT := A[L];

          repeat
            while a[i] <= PIVOT do inc(i);
            while a[j] > PIVOT do dec(j);
            if i < j then
              begin
                t := A[i];
                A[i] := a[j];
                A[j] := t;
              end;
          until i > j;

          a[l] := A[j];
          a[j] := PIVOT;

          quicksort(L, j-1);
          quicksort(i, R);
        end;
    end;

  procedure bsearch(number, lowend, highend: integer; var location: integer;
                    var found: boolean);
    var
      midpoint: integer;
    begin
      if lowend > highend then
        found := false
      else
        begin
          midpoint := (lowend + highend) div 2;
          if number = a[midpoint] then
            begin
              found := true;
              location := midpoint;
            end
          else if number < a[midpoint] then
            bsearch(number, lowend, midpoint-1, location, found)
          else if number > a[midpoint] then
            bsearch(number, midpoint+1, highend, location, found);
        end;
    end;

  begin
    randomize;
    assign(outfile, 'LOCATION.TXT');
    rewrite(outfile);

    for c := 1 to 1000 do
      a[c] := random(10000) + 1;

    quicksort(1, 1000);

    for c := 1 to 500 do
      begin
        number := random(10000) + 1;
        bsearch(number, 1, 1000, location, found);
        if found then
          writeln(outfile, c, ') ', number, ' was found at position ',
                  location, '.');
      end;

    close(outfile);

end.

Intro
=====
This will be the first of several parts on the use of pointers...This
part will contain the basic idea behind pointers and their use.  I
would like feedback from anyone about how well they felt they learned
from this tutorial, and the next two.  They all will be about pointers,
and I want to be sure I am doing OK in explaining them coherently. :>

The Concept of a Pointer
========================
A pointer is simply what it implies by the name.  Something that points.
A pointer is a 4 byte double word that stores the segment and offset in
memory where the variable is located.

We have referred to before a method of getting more than 64K in a data
structure, in the array section.  This is a way.  A pointer is placed on
what is referred to as the heap.  The heap is the remainder of conventional
memory not used by the program itself, the data stack, or any TSRs that
may be on the system at any one time.  The data stack is where variables
are normally allocated directly by name and can be a maximum of 64K in size.

Why would we want to use pointers?  Well, we have already eluded to one
reason, to get around the 64K data limit.  Another reason is to dynamically
allocate memory space.  Basically, any variables declared by name(s) in a
program is allocated at the initial start-up of a program, and is allocated
until the end of the program.  For a lengthy program with a lot of variables,
we may not want all variables to be allocated for something at the start.
Therefore, we can, with pointers, allocate and deallocate memory anytime
we wish in a program.

If we want to save memory in the data stack, 1 4-byte pointer compared to,
say, an array of 1000 integers would be preferable.  That is one function
of a pointer.

In using pointers, we must remember that they are not direct variables,
but addresses of variables.  Let us look at the first example program
below to see exactly what I mean by the last statement.  We also will
see how to properly create variable space address pointers in memory,
address that variable space, and then deallocate it.

program pointers_one;

  type
    strptr = ^string;
  var
    a, b, c: strptr;

  begin
    new(a);
    a^ := 'Turbo Pascal';
    new(b);
    b^ := 'is fun!!!!!!';

    writeln('a is now "', a^, '".');
    writeln('b is now "', b^, '".');

    new(c);
    c^ := a^;
    a^ := b^;
    b^ := c^;
    dispose(c);

    writeln;
    writeln('a is now "', a^, '".');
    writeln('b is now "', b^, '".');
    dispose(a);dispose(b);

  end.

We see the use of several use conventions in the program above.  We have
three pointers listed to be pointers of strings.  (as a note, any data
type that will eventually be a pointer should NORMALLY be defined under
the type declaration to facilitate the use of pointers in procedures.
We will see this later.)

In the declaration to allocate the pointers, we used the ^ sign first, then
the datatype.  We say that the pointer will eventually point to a string
variable.

To address each of the pointer variables, we used the ^ sign after the
particular variable name.  The ^ sign references the variable that the
pointer is pointing to, and NOT the pointer itself.  You may see this
by doing a debug trace for a, b, and c on that program.  You will
clearly see that the pointer's contents is NOT what the variable's
contents is...

The next thing to note is the assignment statements.  For a pointer
variable, we are not assigning direct values, but MOVING THE POINTER
address.  When we say a^ := c^, we are moving the pointer c to point
to the value that a points to.  **WE ARE NOT DIRECTLY CHANGING THE
VALUE OF THAT LOCATION IN MEMORY!!!**.

The last thing to note is the strategically placed uses of the functions
new() and dispose().  These are new functions to us that we will learn
and make use of in the system unit.  The new() function will allocate
the space for the pointer listed in the variable on the heap.  The
dispose() function will deallocate the space for the pointer on the heap.
As you can see, we created and removed the variables as we needed them.

The next thing you were probably wondering with pointers is how to get
a pointer to not point to anything.  Make it address a reserved word
called nil.  If I want pointer p to not point to anything, then I would
write:

        p := nil;

Whatever you do, do not set a pointer to nil before you dispose of the
pointer.  The system will lose track of the variable on the heap, and
therefore is LOST to the system.  Keep this issue in mind as we do
our work and examples with pointers.

Look up getmem() and freemem().

Another issue that wasn't covered above is how we determine how much
memory is in the heap.  Use the functions memavail and maxavail for
the purpose of finding out how much memory you can allocate and if the
pointer data structure is big enough, BE SURE TO CHECK AND SEE IF YOU
HAVE THE MEMORY TO ALLOCATE before you allocate it for a big structure.

Procedure or Function Pointers
==============================
A procedure or function may also be addressed as a pointer.  For that,
as you will see in the example, we can use a declaration for a variable
exactly like the procedure or function declaration.  It is very useful
to minimize code usage, and make things multi-functional.  This sheds
some light into the inner workings of the write command, as was wondered
in c.l.p.b. a little while ago...

program pointer_two;
  type
    compfunc = function(a, b: integer):integer;
  var
    compute: compfunc;
    first, second: integer;
    choice: char;

  {$F+}
  function compadd(a, b: integer): integer;
    begin
      compadd := a + b;
    end;
  function compsub(a, b: integer): integer;
    begin
      compsub := a - b;
    end;
  function compmult(a, b: integer): integer;
    begin
      compmult := a * b;
    end;
  function compdiv(a, b: integer): integer;
    begin
      compdiv := a div b;
    end;

{$F-}

  begin
    @compute := nil;
    write('Enter a first number: ');
    readln(first);
    write('Enter a second number: ');
    readln(second);
    write('Enter +, -, *, or / as a operation: ');
    readln(choice);
    case choice of
      '+': compute := compadd;
      '-': compute := compsub;
      '*': compute := compmult;
      '/': compute := compdiv;
    else
      writeln('Enter a correct option.');
    end;
    compute(first, second);
    writeln(first, ' ', choice, ' ',second,' = ',compute(first, second));

  end.
As you can see, we are assigning a particular function to the function
named compute.  After that, compute performs the specified function of
the function that we just assigned to it.  The rule is that those FAR
declarations MUST be present.  As well, you can see how to refer to the
procedure pointer (as @procedure).  As long as the declarations are
similar for all the functions, we can readdress functions/procedures
using a variable like we did above.

We see that we can probably cut down on a lot of code by using this
method if we had several different ways of doing things, with large
amounts of code for each thing.  For example, if we wanted to sort
something based on user input dependent upon many different methods
or types (say sort increasing by name, decreasing by name, increasing
on size, by date, et.al), we can set up our swapping procedure to be
the way the compute procedure is above.

Now, we will lead into a special usage of a pointered procedure called
exitproc.

ExitProc
========
This is a special defined procedure pointer in Pascal that will determine
the procedures that will be performed upon an exit, NO MATTER what happens,
EVEN IF THERE IS A RUN-TIME ERROR.  Here is your method on being able to
catch and log those run-time errors either to the screen, or to an errors
log.  The primary usage of an exit procedure, though, is to perform any
maintenance that needs to be done upon termination that may not get done
upon an abnormal termination, such as closing files. The way things work
are basically the same as above...

Here is a short example...I will force a run-time error in this program,
so we can see that the end gets run anyway.  As you may see, exitproc
is defined as a pointered procedure when we work with it.  The rest is
basically documented.

program pointer_three;

  var
    exitsave: procedure;
    afile: text;

  {$F+}    { must be far }
  procedure myexit; {must be parameterless}
    begin
      if exitcode <> 0 then
        writeln('There was a problem.');
      writeln('We are exiting...');
    end;
  {$F-}

  begin
    @exitsave := exitproc;  { saving the original exit procedure }
    exitproc := @myexit;    { set to new exit procedure }

    { I am placing a couple of error situations in here so you can
      experiment with what is going on...try it without errors too! }

    {1 - here is a standard file not found RTE.}
    assign(afile, '()()()().!!!');
    reset(afile);

    {2 - here is a division by zero.}
    { writeln(3 / 0); }

    exitproc := @exitsave;  { set the original exit procedure back }
  end.

Here is enough material that I believe that is enough to digest for right
now on use of pointers.  Practice allocating and using pointers very
heavily, as we will get into more advanced issues of using pointers in the
next two parts...Remember that ANY structure can be placed as a pointer,
except for files, and remember to define types for all pointers.

Practice Programming Problem #17
================================
Randomly generate 15000 numbers from 1-25000 into an array.
Then generate another 10000 numbers from 1-25000.
If there is a number from the second set of numbers that happens to be in
the first set of numbers, write out to the file named LOCAT2.TXT something
such as:

13131 was found at position 12000.

Only indicate the first instance of the number you encounter.
You may wish to write a "redrawing bar" for a process indicator.

Note: Be sure with the allocation for those first 15000 numbers that you
keep in line with the topic of this tutorial.

Next Time
=========
We will cover the concept of linked lists, or chained lists.  Practice
very heavily the idea of pointers, because you will need use of them
again in the next section.  E-mail ggrotz@2sprint.net with your comments.



[Back to TUTOR SWAG index]  [Back to Main SWAG index]  [Original]