5.1 Introduction:
Pointers
are variables that hold address values.
Pointers are defined using an asterisk (*) to mean pointer to. A data type is
always included in pointer definitions, since the compiler must know what is
being pointed to, so that it can perform arithmetic correctly on the
pointer. We access the thing pointed to
using the asterisk in a different way, as the indirection operator, meaning
contents of the variable pointed to by.
5.2 Addresses and Pointers:
The ideas behind
pointers are not complicated. Here’s the first key concept: Every byte in the
computer’s memory has an address. Addresses
are numbers, just as they are for houses on a street. The numbers start at 0
and go up from there-l, 2, 3, and so on. If you have 640K of memory, the
highest address is 655,359; for 1 MB of memory, it is 1,048,575.
Your program, when it is loaded into memory, occupies a
certain range of these addresses. That means that every variable and every
function in your program starts at a particular address.
2.3 The Address Of Operator &:
You can find out the
address occupied by a variable by using the address
of operator &. Here's a short program that demonstrates this:
// address of
variables
#
include<iostream.h>
void main ( )
{
int var1 = 11; // define
and initialize
int var2 = 22; II three
variables
int var3 = 33;
cout << endl << &var1 II print
out the addresses
<<endl <<&var2 II of
these variables
<<endl <<&var3;
}
This simple program
defines three integer, variables and initializes them to the values 11, 22, and
33. It then prints out the addresses of these variables. The actual addresses
occupied by the variables in a program depend on many factors, such as the
computer the program is running on, the size of the operating system, and
whether any other programs are currently in memory. For these reasons you
probably won't get the same addresses we did when you run this program. Here's
the output on our machine:
Ox8f4ffff4 address of varl
The address of a variable is not at all the
same as its contents. The contents of
the three variables are 11,22, and 33. The << insertion operator
interprets the addresses in hexadecimal arithmetic, as indicated by the prefix Ox before each number. This is the
usual way to show memory addresses.
The addresses appear in descending order because
automatic variables are stored on the stack, which grows downward in memory. If
we had used external variables they would have ascending addresses, since
external variables are stored on the heap, which grows upward.
2.4 Pointer
Variable:
Address by themselves are rather limited. It’s nice to
know that we can find out where things are in memory, but printing out address
values is not all that useful. The potential for increasing our programming
power requires an additional idea: variables that hold address values.
Addresses are stored similarly as variables of different data types. A variable
that holds an address value is called a pointer variable or simply a pointer.
2.5 Declaring and Initializing Pointers:
Let's examine the
process of declaring pointers. A computer needs to keep track of the type of
value to which a pointer refers. For example, the address of a char looks the
same as the address of a double, but char and double use different numbers of
bytes and different internal formats for storing values. Therefore, a pointer
declaration must specify what type of data it is to which the pointer points.
For example, the statement.
int * p_updates;
This states that the combination * p-updates is type int. Because the * operator is used by applying it to a pointer, the p_updates variable itself must be a pointer. We say that p_updates points to type int. We also say that the type for p_updates is pointer-to-int or, more concisely, int *. To repeat p_updates is a pointer (an address}, and *p-updates is an int and not a pointer.
Incidentally, the use
of spaces around the * operator are optional. For example:
int* ptr;
This emphasizes the idea that int* is a type, pointer-to-int. Where you put the spaces makes no
difference to the compiler. Be aware, however, that the declaration
int* p1,p2;
Creates one pointer (p1) and one ordinary int variable
(p2)
If you define more than one pointer of the same type on
one line, you need only insert the type-pointed-to once, but you need to place
an asterisk before each variable name:
char * ptr1, * ptr2, * ptr3; // three variables of type char*
Or you can use the
asterisk-next-to-the-name approach
char *ptr1, *ptr2,
*ptr3; // three variables of type
char*
Pointers Must Have a Value: An address like OxHf4ffff4 can be thought of as a pointer constant. A pointer like ptr can be thought of as a pointer variable. Just as the integer
variable var1 can be assigned the
constant value 11, so can the pointer variable ptr be assigned the constant value Ox8f4ffff4.
When we first define
a variable, it holds no value (unless we initialize it at the same time) it may
hold a garbage value, but this has no meaning. In the case of pointers, a
garbage value is the address of something in memory, but probably not of
something that we want. So before a pointer is used, a specific address must be
placed in it.
Following this the
program prints out the value contained in ptr,
which should be the same address printed for &var1.
To summarize: A pointer can hold the address
of any variable of the correct type; However, it must be given some value, otherwise it will point to an
address we don't want it to point to, such as into our program code or the
operating system. Wrong pointer values can result in system crashes and are
difficult to debug, since the compiler gives no warning.
2.6 Accessing the Variable Pointed to:
Suppose that we don’t
know the name of a variable but we do know its address. We can access the
contents of the variable, by using a special syntax (indirection operator) to
access the value of such variable.
# include
<iostream.h>
void main( )
{
int
var1 =11; //two integer
variables
int
var2 = 22;
int*
ptr; // pointer to integers
ptr =
&var1; // pointer
points to var1
cout<<endl<<*ptr; // pointer contents of pointer (11)
ptr =
&var2; // pointer
points to var2
cout<<endl<<*ptr; // pointer contents of pointer (22)
}
When an asterisk is
used in front of a variable name, as it is in the * ptr expression; it is called the indirection operator. It means the
value of the variable pointed to by. Thus the expression *ptr represents the value of the
variable pointed to by ptr. When ptr is set to the address of var1, the expression *ptr has the value 11; since var1 is 11. When ptr is changed to the address of var2, the expression *ptr acquires the value 22, since var1 is 22.You can use a pointer not
only to display a variable's value but to perform any operation you would
perform on the variable directly. The asterisk used as the indirection operator
has a different meaning from the asterisk used to declare pointer variables.
The indirection operator precedes the variable and means value of the variable pointed to by the asterisk used in a declaration means pointer to.
int* ptr; //
declaration:
*ptr =37; II
indirection:
Using the indirection
operator to access the value stored in an address is called indirect addressing, or sometimes dereferencing, the pointer:
int v; II defines variable v of type int
int* p; II
defines p as a pointer to int
p = &v; II
assigns address of variable v to pointer p
v = 3; II assigns 3 to v
*p = 3; II
also assigns 3 to V
2.7 Pointers and Arrays:
There is a close
association between pointers and arrays. The near equivalence of pointers and
array names stems form the pointer arithmetic and how C++ handles arrays
internally. Consider the program below:
# include
<iostream.h> ,
void main( )
{
double wages[3] = {10000.0, 20000.0,
30000.0};
short stacks[3] = {3, 2, 1};
II Here are two ways to
get the address of an array
double * pw = wages; II name of an array = address
short * ps = &stacks[0]; II or
use address operator
II with array element
cout <<"pw = " <<pw
<<" *pw = " <<*pw <<"\n";
pw = pw + 1;
cout <<"add 1 to the pw
pointer: \n';
cout <<"pw = "
<<pw <<", *pw = " <<*pw <<"\n\n”;
cout <<"ps = "
<<ps <<" *ps = " <<*ps <<"\n";
ps = ps + 1;
cout <<"add 1 to the ps
pointer:\n";
cout <<"ps = "
<<ps <<", *ps = " <<*ps <<"\n\n”;
cout <<"access two elements
with array notation \n";
cout <<stacks[0] <<"
" <<stacks[1] <<"\n";
cout <<"access two elements
with pointer notation \n";
cout <<*stacks <<"
" <<*(stacks + 1) <<"\n";
cout <<sizeof wages <<"
= size of wages array \n";
cout <<sizeof pw <<" =
size of pw pointer \n";
}
C++ interprets the
name of an array as the, address of its first element. Thus, the statement
double * pw = wages;
makes pw a pointer to type double
and then initializes pw to wages,
which is the address of the first element of the wages array. For wages, as
with any array, we have the following equality:
wages = &wages[0]
= address of first element of array
Just to show that
this is no jive, the program explicitly uses the address operator in the
expression &stacks [0] to
initialize the ps pointer to the
first element of the stacks array. Next, the program inspects the values of pw and *pw. The first is
an address and the second is the value at that address. Because pw points to the first element, the
value displayed for *pw is that of
the first element, 10000. Then, the program adds 1 to pw. This adds 8 (EO + 8 = E8 in hexadecimal) to the numeric address
value, because double is 8 bytes. This makes pw equal to the address of the second element. Thus, *pw now is 20000, the value of the
second element. After this, the program
goes through similar steps for ps.
This time, because ps points to type
short and because short is 2 bytes, adding 1 to the
pointer increases its value by 2. Again, the result is to make the pointer
point to the next element of the array.
Note: Adding 1 to a pointer variable increases its value by
the number of bytes of the type to which it points.
2.8 Pointers and Strings:
The special
relationship between arrays and pointers extends to strings. Consider the
following code:
char
flower[10] = "rose";
cout
<<flower <<"s are red \n";
The name of an array
is the address of its first element, so flower in the cout statement is the address of the char element containing the character r. The cout object
assumes that the address of a char
is the address of a string, so it prints the character at that address and then
continues printing characters until it runs into the null character (\0). In
short, if you give cout the address
of a character, it prints everything from that character to the first null
character that follows it. The crucial element here is not that flower is an
array name but that flower acts as the address of a char. This implies that you can use a pointer-to-char variable as
an argument to cout, also, because
it, too, is the address of a char.
Of course, that pointer should point to the beginning of a string. We'll check
that out in a moment.
But first, what about
the final part of the preceding cout
statement? If flower actually is the address of the first character of a
string, what is the expression "s
are red\ n"? To be consistent with cout's handling of string output, this quoted string also should be
an address. And it is, for in C++ a quoted string, like an array name, serves
as the address of its first element. The preceding code doesn't really send a
whole string to cout, it just sends
the string address. This means strings in an array; quoted string constants,
and strings described by pointers all are handled equivalently: Each really is
passed along an address. That's certainly less work than passing each and every
character in a string.
// Program to
demonstrate arguments passed by pointer
#include
<iostream.h>
void main( )
{
void
centimize(double*); //
prototype
double var = 10.0; II var has value of 10 inches
cout <<endl
<<"var=" <<var <<" inches";
centimize(&var); II change var to centimeters
cout <<endl
<<"var:" <<var <<“centimeters" ;
}
void
centimize(double* ptrd)
{
*ptrd *= 2.54; II *ptrd is the same as var
}
The function centimize ( ) is declared as taking an
argument that is a pointer to double:
void
centimize(double*) II argument is pointer to double
When main( ) calls the function it supplies
the address of the variable as the argument:
centimize(&var);
Remember
that this is not the variable itself, as it is in passing by reference, but the
variable's address.
Because
the centimize ( ) function is passed
an address, it must use the indirection operator, *ptrd, to access the value stored at this address:
*ptrd *= 2.54; II multiply the contents of ptrd by 2.54
Is the
same as
*ptrd = *ptrd * 2.54; II multiply
the contents of ptrd by 2.54
where the
stand-alone asterisk means multiplication.
Since ptrd contains the address of var, anything done to *ptrd is actually done to var. Passing a pointer as an argument
to a function is in some ways similar to passing a reference. They both permit
the variable in the calling program to be modified by the function. However,
the mechanism is different. A reference is an alias for the original variable,
while a pointer is the address of the variable.
2.9 Using new to create Dynamic Structures:
You’ve seen how it
can be advantageous to create arrays during runtime rather than compile
time. The same holds true for
structures. You need to allocate space for only as many structures as a program
needs during a particular run. The new operator is the tool to use. With it, you can create dynamic structures.
“Dynamic” means the memory is allocated during runtime, not during compilation.
Using new with structures has two
parts: creating the structures and accessing its members. To create a
structure, use the structure type with new.
For example, to create an unnamed structure and assign its address to a
suitable pointer, you can do the following:
inflatable * ps = new
inflatable; // consider inflatable is am
structure
This assigns to ps the address of a chunk of free
memory large enough to hold a structure of the inflatable type. The tricky part is accessing members. When
you create a dynamic structure, you can't use the dot membership operator with
the structure name, because the structure has no name. All you have is its
address. C++ provides an operator just for this situation: the arrow membership operator(->).This
operator, formed by typing a hyphen and then a greater-than symbol, does for
pointers to structures what the dot operator does for structure names. For
example, if ps points to a type
inflatable structure, then ps ->
price is the price member of the pointed-to structure.
This operator obtains
memory from the operating system and returns a pointer to its starting point.
Consider the program shows how new
is used:
# include
<iostream.h>
# include
<string.h> II for strcpy()
void main( )
{
char* str = "Idle hands are the
devil’s workshop.";
int len = strlen(str); II get length of str
char* ptr; II make a pointer to char
ptr = new char[len+1]; II set aside memory: string + '\0'
strcpy(ptr, str); II copy str to new memory area ptr
cout <<endl <<“ptr="
<<ptr; II show that str is now in ptr
delete ptr; II release ptr 's memory
}
The expression
ptr = new
char[len+1];
returns a pointer that points to a section of
memory just large enough to hold the string str, whose length len we
found with the strlen ( ) library
function, plus an extra byte for the null character '\0' at the end of the
string. Remember to use brackets
around the size; the compiler won't object if you use parentheses, but the
results will be incorrect.
2.10 Delete Operator:
If your program
reserves many chunks of memory using new,
eventually all the available memory will be reserved and the system will crash.
To ensure safe and efficient use of memory, a corresponding delete operator that returns memory to
the operating system.
delete
ptr;
returns to the system
whatever memory was pointed to by ptr
. Actually, there is no need for this operator, since memory is automatically
returned when the program terminates. However, suppose you use new in a
function. If the function uses a local variable as a pointer to this memory,
then when the function terminates, the pointer will be destroyed but the memory
will be left as an orphan, taking up space that is inaccessible to the rest of
the program. Thus it is always good practice to delete memory when you're
through with it.
5.11 Pointers to Objects:
Pointers can point to
objects as well as to simple data types. We've seen many examples of objects
defined and given a name, in statements like
Distance
dist;
where an object
called dist is defined to be of the Distance class. Sometimes, however, we
don't know, at the time that we write the program, how many objects we want to
create. When this is the case we can use new
to create objects while the program is running.
// Program to show
accessing of member functions by pointer
# include
<iostream.h>
class Distance
{
private:
int feet;
float inches;
public:
void getdist( ) // get length from user
{
cout <<"\n Enter feet:
"; cin >> feet;
cout <<"Enter inches: ";
cin >> inches;
}
void
showdist( ) // display distance
{ cout <<feet <<"\'-"
<<inches <<'\"'; }
};
void main( )
{
Distance dist; // define a named
Distance object
dist.getdist( ); // access object
members
dist.showdist( ); // with dot operator
Distance* distptr; //
pointer to Distance
distptr = new
Distance; // points to new
Distance object
distptr->getdist(
); // access object
members
distptr->showdist(
); // with -> operator
}
This program uses a
variation of the Distance class seen
in previous chapters. The main ( )
function defines dist, uses the
Distance member functions, getdist ( )
to get a distance from the user; and
then uses showdist ( ) to display
it.
Referring To Members: The program creates another object of type Distance the new operator. To refer to a member function dot operator requires the identifier on its left to be a variable. We can deference (get the contents of the variable pointed to by) the pointer:
(*distptr).getdist(
); // ok but
inelegant
However, this is slightly
cumbersome because of the parentheses. A more concise
approach is furnished
by the membership-access operator ->,
which consists of a hyphen and a greater than sign:
distptr->getdist(
); // better
approach
Exercise:
- The structure candy contains three members. The first holds the name of the candy, the second holds the price (can be fractional) and the third holds the weight. Write a program that creates a variable of candy using the new operator.
- Implement the concept of creating Linked list.
- Write a program to insert and delete a number in a linked list.
- Write a program to implement the stack, using pointer.