|
 |
[Introduction] [Chapter 1] [Chapter
2] [ Chapter 3 ] [Chapter
4] [Chapter 5] [Chapter
6] [Chapter 7] [Chapter
8] [Chapter 9] [Chapter
10] [Chapter 11] [Chapter
12]
Chapter
3: Pointers
Because pointers are so important in C and C++, this chapter will
review some of the more important topics concerning pointers. Even
if you are extremely conversant in the use of pointers, you should
not completely ignore this chapter because some new material unique
to C++ is presented here.
POINTER REVIEW
Example program > POINTERS.CPP
Examine the program named POINTERS.CPP for a simple example of the
use of pointers. This is a pointer review and if you are comfortable
with the use of pointers, you can skip this example program completely.
A pointer in either ANSI-C or C++ is declared with an asterisk preceding
the variable name. The pointer is then a pointer to a variable of
that one specific type and should not be used with variables of other
types. Thus pt_int is a pointer to an integer type variable and should
not be used with any other type. Of course, an experienced C programmer
knows that it is simple to coerce the pointer to be used with some
other type by using a cast, but he must then assume the responsibility
for its correct usage.

In line 12 the pointer named pt_int is assigned the address of the variable named pig
and line 13 uses the pointer named pt_int to add the value of dog
to the value of pig because the asterisk dereferences the pointer in exactly the same manner as
standard C. Figure 3-1 is a graphical representation of the data space
following execution of line 13. Note that a box containing a dot represents
a pointer. The address is used to print out the value of the variable
pig in line 14 illustrating the use of a pointer with the output stream object cout.
Likewise, the pointer to float named pt_float is assigned the address
of x, then used in a trivial calculation in line 18.
CONSTANT POINTERS AND POINTERS TO CONSTANTS
The definition of C++ allows a pointer to a constant to be defined
such that the value to which the pointer points cannot be changed
but the pointer itself can be moved to another variable or constant.
The method of defining a pointer to a constant is illustrated in line
22. In addition to a pointer to a constant, you can also declare a
constant pointer, one that cannot be changed. Line 23 illustrates
this. Note that neither of these pointers are used in illustrative
code.
Either of these constructs can be used to provide additional compile
time checking and improve the quality of your code. If you know a
pointer will never be moved due to its nature, you should define it
as a constant pointer. If you know that a value will not be changed,
it can be defined as a constant and the compiler will tell you if
you ever inadvertently attempt to change it.
A POINTER TO VOID
The pointer to void is actually
a part of the ANSI-C standard but is relatively new so it is commented
upon here. A pointer to void can
be assigned the value of any other pointer type. You will notice that
the pointer to void named general
is assigned an address of an int
type in line 15 and the address of a float
type in line 20 with no cast and no complaints from the compiler.
This is a relatively new concept in C and C++. It allows a programmer
to define a pointer that can be used to point to many different kinds
of things to transfer information around within a program. A good
example is the malloc() function
which returns a pointer to void.
This pointer can be assigned to point to any entity, thus transferring
the returned pointer to the correct type.
A pointer to void is aligned
in memory in such a way that it can be used with any of the simple
predefined types available in C++, or in ANSI-C for that matter. They
will also align with any compound types the user can define since
compound types are composed of the simpler types.
If you are not completely comfortable with this trivial program using
pointers, you should review the use of pointers in any good C programming
book or Coronado Enterprises C tutorial before proceeding on because
we will assume that you have a thorough knowledge of pointers throughout
the remainder of this tutorial. It is not possible to write a C program
of any significant size or complexity without the use of pointers.
Be sure to compile and execute this program.
DYNAMIC ALLOCATION AND DEALLOCATION
Example program > NEWDEL.CPP
Examine the program named NEWDEL.CPP for our first example of the
new and delete
operators. The new and
delete operators do dynamic
allocation and deallocation in much the same manner that the malloc()
and free() functions do in your
old favorite C implementation.
During the design of C++, it was felt that since dynamic allocation
and deallocation are such a heavily used part of the C programming
language and would also be heavily used in C++, it should be a part
of the language, rather than a library add-on. The new
and delete operators
are actually a part of the C++ language and are operators, much like
the addition operator or the assignment operator. They are therefore
very efficient, and are very easy to use as we will see in this example
program.
Lines 15 and 16 illustrate the use of pointers in the tradition of
C and line 17 illustrates the use of the new
operator. This operator requires one modifier which must be
a type as illustrated here. The pointer named point2
is now pointing at the dynamically allocated integer variable
which exists on the heap, and can be used in the same way that any
dynamically allocated variable is used in ANSI-C. Lines 19 and 20
illustrate displaying the value on the monitor which was assigned
in line 18.
Line 21 allocates another new variable and line 22 causes point2
to refer to the same dynamically allocated variable as point1
is pointing to. In this case, the reference to the variable
that point2 was previously pointing
to has been lost and it can never be used or deallocated. It is lost
on the heap until we return to the operating system when it will be
reclaimed for further use, so this is obviously not good practice.
Note that point1 is deallocated
with the delete operator in
line 26, and point2 can not
be deleted since it is now pointing to nothing. Since the pointer
point1 itself is not changed,
it is probably still pointing to the original data on the heap. This
data could probably be referred to again using point1,
but it would be terrible programming practice since you have no guarantee
what the system will do with the pointer or the data. The data storage
is returned to the free list to be allocated in a subsequent call,
and will soon be reused in any practical program.
Since the delete operator is
defined to do nothing if it is passed a NULL value, it is legal to
ask the system to delete the data pointed to by a pointer with the
value of NULL, but nothing will actually happen. It is actually wasted
code. The delete operator can
only be used to delete data allocated by a new
operator. If the delete is
used with any other kind of data, the operation is undefined and anything
can happen. According to the ANSI standard, even a system crash is
a legal result of this illegal operation, and can be defined as such
by the compiler writer.
In line 28, we declare some floating point variables. You will remember
that in C++ the variables do not have to be declared at the beginning
of a block. A declaration is an executable statement and can therefore
appear anywhere in a list of executable statements. One of the float
variables is allocated within the declaration to illustrate
that this can be done. Some of the same operations are performed on
these float type variables as
were done on the int types earlier.
Some examples of the use of a structure are given in lines 36 through
44 and should be self explanatory.
Finally, since the new operator
requires a type to determine the size of the dynamically allocated
block, you may wonder how you can allocate a block of arbitrary size.
This is possible by using the construct illustrated in line 48 where
a block of 37 char sized entities,
which will be 37 bytes, is allocated. A block of 133 bytes greater
than the size of the date structure is allocated in line 50. It is
therefore clear that the new operator
can be used with all of the flexibility of the malloc()
function which you are familiar with. The brackets are required in
lines 49 and 51 to tell the compiler that it is deallocating an array.
The standard functions which you have been using in C for dynamic
memory management, malloc(), calloc(), and free(),
are also available for use in C++ and can be used in the same manner
they were used in C. If you are updating code with the older function
calls, continue to use them for any additions to the code. If you
are designing and coding a new program you should use the newer constructs
because they are a built in part of the language rather than an add
on and are therefore more efficient. It is an error to delete
a variable that has been malloc'ed
and it is an error to free a
variable that was allocated with new.
Be sure to compile and execute this program.
POINTERS TO FUNCTIONS
Example program > FUNCPNT.CPP
Examine the program named FUNCPNT.CPP for an example of using a pointer
to a function. It must be pointed out that there is nothing new here,
the pointer to a function is available in ANSI-C as well as in C++
and works in the manner described here for both languages. It is not
regularly used by most C programmers, so it is briefly discussed here
as a refresher. If you are comfortable with the use of pointers to
functions, you can skip this discussion entirely.
There is nothing unusual about this program except for the pointer
to a function declared in line 7. This declares a pointer to a function
which returns nothing (void)
and requires a single formal parameter, a float
type variable. You will notice that all three of the functions
declared in lines 4 through 6 fit this profile and are therefore candidates
to be called with this pointer. If you have not used prototyping in
C, these lines will look strange to you. Don't worry about them at
this point since we will study prototyping in the next chapter of
this tutorial.
Observe that in line 14 we call the function print_stuff()
with the parameter pi and in
line 15 we assign the function pointer named function_pointer
the value of print_stuff and
use the function pointer to call the same function again in line 16.
Lines 14 and 16 are therefore identical in what is accomplished because
of the pointer assignment in line 15. In lines 17 through 22, a few
more illustrations of the use of the function pointer are given. You
will be left to study these on your own.
Since we assigned the name of a function to a function pointer, and
did not get an assignment error, the name of a function must be a
pointer to that function. This is exactly the case. A function name
is a pointer to that function, but it is a pointer constant and cannot
be changed. This is exactly the case we found when we studied arrays
in ANSI-C at some point in our C programming background. An array
name is a constant pointer to the first element of the array.
Since the name of the function is a constant pointer to that function,
we can assign the name of the function to a function pointer and use
the function pointer to call the function. The only caveat is that
the return value and the number and types of parameters must be identical.
Most C and C++ compilers will not, and in fact, can not warn you of
type mismatches between the parameter lists when the assignments are
made. This is because the assignments are done at runtime when no
type information is available to the system, rather than at compile
time when all type information is available.
The use and operation of pointers must be thoroughly understood when
we get to the material on dynamic binding and polymorphism later in
this tutorial. It will be discussed in detail at that time.
Be sure to compile and execute this program.
PROGRAMMING EXERCISES
- When
dynamically allocated data is deleted, it is still actually in
memory, stored on the heap. Repeat the output statement from lines
24 and 25 of NEWDEL.CPP immediately following the delete in line
26 to see if the values are really still there. Repeat it once
again just prior to the end of the program when the data spaces
should have been written over to see if you get garbage out. Even
if your compiler reports the correct data, it is terrible practice
to count on this data still being there because in a large dynamic
program, the heap space will be used repeatedly.
- Add
a function to FUNCPNT.CPP which uses a single integer for a parameter
and attempt to call it by using the function pointer to see if
you get the correct data into the function.
<< back next >>
[Introduction] [Chapter 1] [Chapter
2] [ Chapter 3 ] [Chapter
4] [Chapter 5] [Chapter
6] [Chapter 7] [Chapter
8] [Chapter 9] [Chapter
10] [Chapter 11] [Chapter
12] |
|