Tải bản đầy đủ (.pdf) (40 trang)

Absolute C++ (phần 11) ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (349.64 KB, 40 trang )

400 Strings
37. The Dying Poet - L. Gottschalk
38. Dead March - G.F. Handel
39. Do They Think of Me At Home - Chas. W. Glover
40. The Dearest Spot - W.T. Wrighton
1. Evening - L. Van Beethoven
2. Embarrassment - Franz Abt
3. Erin is my Home - no author listed
4. Ellen Bayne - Stephen C. Foster
. . .
9. Alla Mazurka- A. Nemerowsky
. . .
1. The Dying Volunteer - A.E. Muse
2. Dolly Day - Stephen C. Foster
3. Dolcy Jones - Stephen C. Foster
4. Dickory, Dickory, Dock - no author listed
5. The Dear Little Shamrock - no author listed
6. Dutch Warbler - no author listed
. . .
The ultimate task is to produce an alphabetized list of composers followed by a list of pieces
by them alphabetized on the title within composer. This exercise is easier if it is broken into
pieces:
Write code to:
a. Remove the lead numbers, any periods, and any spaces so that the first word of the title is
the first word of the line.
b. Replace any multiple spaces with a single space.
c. A few titles may have several - characters, for example:
20. Ba- Be- Bi- Bo- Bu - no author listed
Replace all dash - characters on any line before the end of the line by a space except the
last one.
d. The last word in the title may have the - character with no space between it and the =


character. Put the space in.
e. When alphabetizing the title, you don’t want to consider an initial “A”, “An”, or “The” in
the title. Write code to move such initial words to just before the - character. A comma after
the last word in the title is not required, but that would be a nice touch. This can be done
after the composer’s names are moved to the front, but obviously the code will be different.
f. Move the composer’s names to the beginning of the line, followed by the - character,
followed by the composition title.
g. Move any first initial, first and second names of the composer to after the composer’s last
name. If the composer is “no author listed” this should not be rearranged, so test for this
combination.
09_CH09.fm Page 400 Wednesday, August 13, 2003 1:04 PM
Programming Projects 401
h. Alphabetize by composer using any sort routine you know. You may ignore any duplicate
composer’s last name, such as CPE Bach and JS Bach, but sorting by composer’s second
name would be a nice touch. You may use the insertion sort, or selection sort, or bubble
sort, or other sorting algorithm.
i. If you have not already done so, move “A”, “An”, or “The” that may begin a title to the end
of the title. Then alphabetize within each composer by composition title.
j. Keep a copy of your design and your code. You will be asked to do this over using the STL
vector container.
09_CH09.fm Page 401 Wednesday, August 13, 2003 1:04 PM

10

Pointers and Dynamic Arrays
10.1 POINTERS 404
Pointer Variables 405
Basic Memory Management 414
Pitfall: Dangling Pointers 416
Dynamic Variables and Automatic Variables 416

Tip: Define Pointer Types 417
Pitfall: Pointers as Call-by-Value Parameters 419
Uses for Pointers 421
10.2 DYNAMIC ARRAYS 422
Array Variables and Pointer Variables 422
Creating and Using Dynamic Arrays 423
Example: A Function That Returns an Array 427
Pointer Arithmetic 429
Multidimensional Dynamic Arrays 430
10.3 CLASSES, POINTERS, AND DYNAMIC ARRAYS 433
The

->
Operator 433
The

this
Pointer 434
Overloading the Assignment Operator 435
Example: A Class for Partially Filled Arrays 437
Destructors 445
Copy Constructors 446
CHAPTER SUMMARY 451
ANSWERS TO SELF-TEST EXERCISES 452
PROGRAMMING PROJECTS 454
this page intentionally blank)

10

Pointers and Dynamic Arrays


Memory is necessary for all the operations of reason.

Blaise Pascal, Pensées
INTRODUCTION

A

pointer

is a construct that gives you more control of the computer’s memory.
This chapter will show you how pointers are used with arrays and will intro-
duce a new form of array called a

dynamically allocated array.

Dynamically
allocated arrays (dynamic arrays for short) are arrays whose size is determined
while the program is running, rather than being fixed when the program is
written.
Before reading Sections 10.1 and 10.2 on pointers and dynamically allo-
cated arrays you should first read Chapters 1 through 6 (omitting the coverage
of vectors if you wish), but you need not read any of Chapters 7 through 9.
You can even read Sections 10.1 and 10.2 after reading just Chapters 1 to 5,
provided you ignore the few passages that mention classes.
Section 10.3 discusses some tools for classes that only become relevant once
you begin to use pointers and dynamically allocated data (such as dynamically
allocated arrays). Before covering Section 10.3, you should read Chapters 1
through 8, although you may omit the coverage of vectors if you wish.
You may cover this chapter, Chapter 11 on separate compilation and

namespaces, Chapter 12 on file I/O, and Chapter 13 on recursion in any
order. If you do not read the Chapter 11 section on namespaces before this
chapter, you might find it profitable to review the section of Chapter 1 enti-
tled “Namespaces.”

Pointers

By indirections find directions out.

William Shakespeare,

Hamlet

A

pointer

is the memory address of a variable. Recall from Chapter 5 that the
computer’s memory is divided into numbered memory locations (called

bytes

)
and that variables are implemented as a sequence of adjacent memory loca-
tions. Recall also that sometimes the C++ system uses these memory addresses
as names for the variables. If a variable is implemented as, say, three memory
locations, then the address of the first of these memory locations is sometimes
10.1
Pointers 405


used as a name for that variable. For example, when the variable is used as a call-by-
reference argument, it is this address, not the identifier name of the variable, that is
passed to the calling function. An address that is used to name a variable in this way (by
giving the address in memory where the variable starts) is called a

pointer

because the
address can be thought of as “pointing” to the variable. The address “points” to the vari-
able because it identifies the variable by telling

where

the variable is, rather than telling
what the variable’s name is.
You have already been using pointers in a number of situations. As noted in the pre-
vious paragraph, when a variable is a call-by-reference argument in a function call, the
function is given this argument variable in the form of a pointer to the variable. As
noted in Chapter 5, an array is given to a function (or to anything else, for that matter)
by giving a pointer to the first array element. (At the time we called these pointers
“memory addresses,” but that is the same thing as a pointer.) These are two powerful
uses for pointers, but they are handled automatically by the C++ system. This chapter
shows you how to write programs that directly manipulate pointers rather than relying
on the system to manipulate the pointers for you.


POINTER VARIABLES

A pointer can be stored in a variable. However, even though a pointer is a memory
address and a memory address is a number, you cannot store a pointer in a variable of

type

int

or

double

. A variable to hold a pointer must be declared to have a pointer
type. For example, the following declares

p

to be a pointer variable that can hold one
pointer that points to a variable of type

double

:

double *p;

The variable

p

can hold pointers to variables of type

double


, but it cannot normally
contain a pointer to a variable of some other type, such as

int

or

char

. Each variable
type requires a different pointer type.

1

In general, to declare a variable that can hold pointers to other variables of a specific
type, you declare the pointer variable just as you would declare an ordinary variable of
that type, but you place an asterisk in front of the variable name. For example, the fol-
lowing declares the variables

p1

and

p2

so they can hold pointers to variables of type

int

; it also declares two ordinary variables


v1

and

v2

of type

int

:

int *p1, *p2, v1, v2;

There must be an asterisk before

each

of the pointer variables. If you omit the second
asterisk in the above declaration, then

p2

will not be a pointer variable; it will instead be
an ordinary variable of type

int

.


1

There are ways to get a pointer of one type into a pointer variable for another type, but it does
not happen automatically and is very poor style anyway.
declaring
pointer
variables
406 Pointers and Dynamic Arrays

When discussing pointers and pointer variables, we usually speak of

pointing

rather
than speaking of

addresses.

When a pointer variable, such as

p1

, contains the address of
a variable, such as

v1

, the pointer variable is said to


point to the variable



v1

or to be

a
pointer to the variable



v1

.



Pointer variables, like

p1

and

p2

declared above, can contain pointers to variables
like


v1

and

v2

. You can use the operator

&

to determine the address of a variable, and
you can then assign that address to a pointer variable. For example, the following will
set the variable

p1

equal to a pointer that points to the variable

v1

:

p1 = &v1;

You now have two ways to refer to

v1

: You can call it


v1

or you can call it “the variable
pointed to by

p1

.” In C++, the way you say “the variable pointed to by

p1

” is

*p1

. This
is the same asterisk that we used when we declared

p1

, but now it has yet another
meaning. When the asterisk is used in this way it is called the

dereferencing operator

,
and the pointer variable is said to be

dereferenced


.
P
OINTER
V
ARIABLE
D
ECLARATIONS
A variable that can hold pointers to other variables of type
Type_Name
is declared similar to the
way you declare a variable of type
Type_Name
, except that you place an asterisk at the begin-
ning of the variable name.
S
YNTAX
Type_Name

*
Variable_Name1

, *
Variable_Name2

,

. . .;
E
XAMPLE
double *pointer1, *pointer2;

A
DDRESSES

AND
N
UMBERS
A pointer is an address, and an address is an integer, but a pointer is not an integer. That is not
crazy—that is abstraction
! C++ insists that you use a pointer as an address and that you not use it
as a number. A pointer is not a value of type
int or of any other numeric type. You normally can-
not store a pointer in a variable of type
int. If you try, most C++ compilers will give you an error
message or a warning message. Also, you cannot perform the normal arithmetic operations on
pointers. (As you will see later in this chapter, you can perform a kind of addition and a kind of
subtraction on pointers, but they are not the usual integer addition and subtraction.)
the & operator
the * operator
dereferencing
operator
Pointers 407
Putting these pieces together can produce some surprising results. Consider the fol-
lowing code:
v1 = 0;
p1 = &v1;
*p1 = 42;
cout << v1 << endl;
cout << *p1 << endl;
This code will output the following to the screen:
42

42
As long as p1 contains a pointer that points to v1, then v1 and *p1 refer to the same
variable. So when you set
*p1 equal to 42, you are also setting v1 equal to 42.
The symbol
& that is used to obtain the address of a variable is the same symbol that
you use in function declarations to specify a call-by-reference parameter. This is not a
coincidence. Recall that a call-by-reference argument is implemented by giving the
address of the argument to the calling function. So, these two uses of the symbol
& are
very closely related, although they are not exactly the same.
P
OINTER
T
YPES
There is a bit of an inconsistency (or at least a potential for confusion) in how C++ names pointer
types. If you want a parameter whose type is, for example, a pointer to variables of type
int,
then the type is written
int*, as in the following example:
void manipulatePointer(int* p);
If you want to declare a variable of the same pointer type, the * goes with the variable, as in the
following example:
int *p1, *p2;
In fact, the compiler does not care whether the * is attached to the int or the variable name, so
the following are also accepted by the compiler and have the same meanings:
void manipulatePointer(int *p);//Accepted but not as nice.
int* p1, *p2;//Accepted but dangerous.
However, we find the first versions to be clearer. In particular, note that when declaring variables
there must be one

* for each pointer variable.
408 Pointers and Dynamic Arrays
You can assign the value of one pointer variable to another pointer variable. For
example, if
p1 is still pointing to v1, then the following will set p2 so that it also points
to
v1:
p2 = p1;
Provided we have not changed v1’s value, the following will also output 42 to the
screen:
cout << *p2;
Be sure you do not confuse
p1 = p2;
and
*p1 = *p2;
When you add the asterisk, you are not dealing with the pointers p1 and p2, but with
the variables to which the pointers are pointing. This is illustrated in Display 10.1, in
which variables are represented as boxes and the value of the variable is written inside
the box. We have not shown the actual numeric addresses in the pointer variables
because the numbers are not important. What is important is that the number is the
address of some particular variable. So, rather than use the actual number of the
T
HE

*

AND

&
O

PERATORS
The * operator in front of a pointer variable produces the variable to which it points. When used
this way, the
* operator is called the dd
dd
ee
ee
rr
rr
ee
ee
ff
ff
ee
ee
rr
rr
ee
ee
nn
nn
cc
cc
ii
ii
nn
nn
gg
gg



oo
oo
pp
pp
ee
ee
rr
rr
aa
aa
tt
tt
oo
oo
rr
rr
.
The operator
& in front of an ordinary variable produces the address of that variable; that is, it
produces a pointer that points to the variable. The
& operator is simply called the aa
aa
dd
dd
dd
dd
rr
rr
ee

ee
ss
ss
ss
ss


oo
oo
ff
ff


oo
oo
pp
pp
ee
ee
rr
rr
aa
aa
tt
tt
oo
oo
rr
rr
.

For example, consider the declarations
double *p, v;
The following sets the value of p so that p points to the variable v:
p = &v;
*p
produces the variable pointed to by p, so after the above assignment, *p and v refer to the
same variable. For example, the following sets the value of
v to 9.99, even though the name v is
never explicitly used:
*p = 9.99;
pointers in
assignment
statements
Pointers 409
address, we have merely indicated the address with an arrow that points to the variable
with that address.
Since a pointer can be used to refer to a variable, your program can manipulate vari-
ables even if the variables have no identifiers to name them. The operator
new can be
used to create variables that have no identifiers to serve as their names. These nameless
variables are referred to via pointers. For example, the following creates a new variable
of type
int and sets the pointer variable p1 equal to the address of this new variable
(that is,
p1 points to this new, nameless variable):
p1 = new int;
This new, nameless variable can be referred to as *p1 (that is, as the variable pointed to
by
p1). You can do anything with this nameless variable that you can do with any other
Pointer Variables Used with =

If p1 and p2 are pointer variables, then the statement
p1 = p2;
changes the value of p1 so that it is the memory address (pointer) in p2. A common way to think
of this is that the assignment will change
p1 so that it points to the same thing to which p2 is cur-
rently pointing.
Display 10.1 Uses of the Assignment Operator with Pointer Variables
p1 = p2;
*p1 = *p2;
Before: After:
p1
p2
p1
p2
p1
p2
p1
p2
8
9
8
9
8
Before:
9
9
After:
9
new
410 Pointers and Dynamic Arrays

variable of type int. For example, the following code reads a value of type int from the
keyboard into this nameless variable, adds
7 to the value, and then outputs this new
value:
cin >> *p1;
*p1 = *p1 + 7;
cout << *p1;
The new operator produces a new, nameless variable and returns a pointer that
points to this new variable. You specify the type for this new variable by writing the
type name after the
new operator. Variables that are created using the new operator are
called dynamically allocated variables or simply dynamic variables, because they are
created and destroyed while the program is running. The program in Display 10.2
demonstrates some simple operations on pointers and dynamic variables. Display 10.3
graphically illustrates the working of the program in Display 10.2.
T
HE

new
O
PERATOR
The new operator creates a new dynamic variable of a specified type and returns a pointer that
points to this new variable. For example, the following creates a new dynamic variable of type
MyType and leaves the pointer variable p pointing to this new variable:
MyType *p;
p = new MyType;
If the type is a class type, the default constructor is called for the newly created dynamic variable.
You can specify a different constructor by including arguments as follows:
MyType *mtPtr;
mtPtr = new MyType(32.0, 17); // calls MyType(double, int);

A similar notation allows you to initialize dynamic variables of nonclass types, as illustrated
below:
int *n;
n = new int(17); // initializes *n to 17
With earlier C++ compilers, if there was insufficient available memory to create the new variable,
then
new returned a special pointer named NULL. The C++ standard provides that if there is insuf-
ficient available memory to create the new variable, then the
new operator, by default, termi-
nates the program.
dynamic
variable
Pointers 411
Display 10.2 Basic Pointer Manipulations
1 //Program to demonstrate pointers and dynamic variables.
2 #include <iostream>
3 using std::cout;
4 using std::endl;
5 int main(
)
6 {
7 int *p1, *p2;
8 p1 = new int;
9 *p1 = 42;
10 p2 = p1;
11 cout << "*p1 == " << *p1 << endl;
12 cout << "*p2 == " << *p2 << endl;
13 *p2 = 53;
14 cout << "*p1 == " << *p1 << endl;
15 cout << "*p2 == " << *p2 << endl;

16 p1 = new int;
17 *p1 = 88;
18 cout << "*p1 == " << *p1 << endl;
19 cout << "*p2 == " << *p2 << endl;
20 cout << "Hope you got the point of this example!\n";
21 return 0;
22 }
S
AMPLE
D
IALOGUE
*p1 == 42
*p2 == 42
*p1 == 53
*p2 == 53
*p1 == 88
*p2 == 53
Hope you got the point of this example!
412 Pointers and Dynamic Arrays
When the new operator is used to create a dynamic variable of a class type, a con-
structor for the class is invoked. If you do not specify which constructor to use, the
default constructor is invoked. For example, the following invokes the default con-
structor:
SomeClass *classPtr;
classPtr = new SomeClass; //Calls default constructor.
Display 10.3 Explanation of Display 10.2
p1
p2
(c)
*p1 = 42;

42
?
p1
p2
(b)
p1 = new int;
?
?
p1
p2
(a)
int *p1, *p2;
?
p1
p2
(d)
p2 = p1;
42
?
p1
p2
(g)
*p1 = 88;
88
53
p1
p2
(e)
*p2 = 53;
53

p1
p2
(f)
p1 = new int;
?
53
Pointers 413
Self-Test Exercises
If you include constructor arguments, you can invoke a different constructor, as illus-
trated below:
classPtr = new SomeClass(32.0, 17);//Calls SomeClass(double, int).
A similar notation allows you to initialize dynamic variables of nonclass types, as illus-
trated below:
double *dPtr;
dPtr = new double(98.6); // Initializes *dPtr to 98.6.
A pointer type is a full-fledged type and can be used in the same ways as other types.
In particular, you can have a function parameter of a pointer type and you can have a
function that returns a pointer type. For example, the following function has a parame-
ter that is a pointer to an
int variable and returns a (possibly different) pointer to an
int variable:
int* findOtherPointer(int* p);
1. What is a pointer in C++?
2. Give at least three uses of the * operator. Name and describe each use.
3. What is the output produced by the following code?
int *p1, *p2;
p1 = new int;
p2 = new int;
*p1 = 10;
*p2 = 20;

cout << *p1 << " " << *p2 << endl;
p1 = p2;
cout << *p1 << " " << *p2 << endl;
*p1 = 30;
cout << *p1 << " " << *p2 << endl;
How would the output change if you were to replace
*p1 = 30;
with the following?
*p2 = 30;
4. What is the output produced by the following code?
int *p1, *p2;
p1 = new int;
pointer
parameters
414 Pointers and Dynamic Arrays
p2 = new int;
*p1 = 10;
*p2 = 20;
cout << *p1 << " " << *p2 << endl;
*p1 = *p2; //This is different from Exercise 4
cout << *p1 << " " << *p2 << endl;
*p1 = 30;
cout << *p1 << " " << *p2 << endl;

BASIC MEMORY MANAGEMENT
A special area of memory, called the freestore or the heap, is reserved for dynamically
allocated variables. Any new dynamic variable created by a program consumes some of
the memory in the freestore. If your program creates too many dynamic variables, it
will consume all the memory in the freestore. If this happens, any additional calls to
new will fail. What happens when you use new after you have exhausted all the memory

in the freestore (all the memory reserved for dynamically allocated variables) will
depend on how up-to-date your compiler is. With earlier C++ compilers, if there was
insufficient available memory to create the new variable, then
new returned a special
value named
NULL. If you have a compiler that fully conforms to the newer C++ stan-
dard, then if there is insufficient available memory to create the new variable, the
new
operator terminates the program. Chapter 18 discusses ways to configure your program
so that it can do things other than abort when
new exhausts the freestore.
2
If you have an older compiler, you can check to see if a call to new was successful by
testing to see if
NULL was returned by the call to new. For example, the following code
tests to see if the attempt to create a new dynamic variable succeeded. The program will
end with an error message if the call to
new failed to create the desired dynamic variable:
int *p;
p = new int;
if (p == NULL)
{
cout << "Error: Insufficient memory.\n";
exit(1);
}
//If new succeeded, the program continues from here.
(Remember that since this code uses exit, you need an include directive for the library
with header file
<cstdlib> or, with some implementations, <stdlib.h>.)
2

Technically, the new operator throws an exception, which, if not caught, terminates the pro-
gram. It is possible to catch the exception and handle the exception
. Exception handling is dis-
cussed in Chapter 18.
freestore or
heap
Pointers 415
The constant NULL is actually the number 0, but we prefer to think of it and spell it
as
NULL make it clear that you mean this special-purpose value which you can assign to
pointer variables. We will discuss other uses for
NULL later in this book. The definition
of the identifier
NULL is in a number of the standard libraries, such as <iostream> and
<cstddef>, so you should use an include directive for either <iostream>, <cstddef>
(or another suitable library) when you use NULL.
As we said,
NUll is actually just the number 0. The definition of NULL is handled by
the C++ preprocessor which replaces
NULL with 0. Thus, the compiler never actually
sees “
NULL” and so there is no namespace issue and no using directive is needed for
NULL.
3
While we prefer to use NULL rather than 0 in our code, we note that some
authorities hold just the opposite view and advocate using
0 rather than NULL.
(Do not confuse the
NULL pointer with the null character ’\0’ which is used to ter-
minate C strings. They are not the same. One is the integer

0 while the other is the
character
’\0’.)
Newer compilers do not require the above explicit check to see if the new dynamic vari-
able was created. On newer compilers, your program will automatically end with an error
message if a call to
new fails to create the desired dynamic variable. However, with any
compiler, the above check will cause no harm and will make your program more portable.
The size of the freestore varies from one implementation of C++ to another. It is
typically large, and a modest program is not likely to use all the memory in the
freestore. However, even in modest programs it is a good practice to recycle any
freestore memory that is no longer needed. If your program no longer needs a dynamic
variable, the memory used by that dynamic variable can be returned to the freestore
manager which recycles the memory to create other dynamic variables. The
delete
operator eliminates a dynamic variable and returns the memory that the dynamic vari-
able occupied to the freestore manager so that the memory can be reused. Suppose that
p is a pointer variable that is pointing to a dynamic variable. The following will destroy
the dynamic variable pointed to by
p and return the memory used by the dynamic vari-
able to the freestore manager for reuse:
delete p;
3
The details are as follows: The definition of NULL uses #define, a form of definition that was
inherited from the C language and that is handled by the preprocessor.
NULL
NULL is a special constant pointer value that is used to give a value to a pointer variable that
would not otherwise have a value.
NULL can be assigned to a pointer variable of any type. The
identifier

NULL is defined in a number of libraries, including <iostream>. (The constant NULL is
actually the integer
0.)
NULL is 0
delete
416 Pointers and Dynamic Arrays
Pitfall
D
ANGLING
P
OINTERS
When you apply delete to a pointer variable, the dynamic variable to which it is pointing is
destroyed. At that point, the value of the pointer variable is undefined, which means that you do
not know where it is pointing. Moreover, if some other pointer variable was pointing to the
dynamic variable that was destroyed, then this other pointer variable is also undefined. These
undefined pointer variables are called dd
dd
aa
aa
nn
nn
gg
gg
ll
ll
ii
ii
nn
nn
gg

gg


pp
pp
oo
oo
ii
ii
nn
nn
tt
tt
ee
ee
rr
rr
ss
ss
. If p is a dangling pointer and your pro-
gram applies the dereferencing operator
* to p (to produce the expression *p), the result is
unpredictable and usually disastrous. Before you apply the dereferencing operator
* to a pointer
variable, you should be certain that the pointer variable points to some variable.
C++ has no built-in test to check whether a pointer variable is a dangling pointer. One way to
avoid dangling pointers is to set any dangling pointer variable equal to
NULL. Then your pro-
gram can test the pointer variable to see if it is equal to
NULL before applying the dereferencing

operator
* to the pointer variable. When you use this technique, you follow a call to delete by
code that sets all dangling pointers equal to
NULL. Remember, other pointer variables may
become dangling pointers besides the one pointer variable used in the call to
delete, so be sure
to set
all
dangling pointers to NULL. It is up to the programmer to keep track of dangling pointers
and set them to
NULL or otherwise ensure that they are not dereferenced.

DYNAMIC VARIABLES AND AUTOMATIC VARIABLES
Variables created with the new operator are called dynamic variables (or dynamically allo-
cated variables) because they are created and destroyed while the program is running.
Local variables—that is, variables declared within a function definition—also have a
certain dynamic characteristic, but they are not called dynamic variables. If a variable is
local to a function, then the variable is created by the C++ system when the function is
called and is destroyed when the function call is completed. Since the
main part of a
program is really just a function called
main, this is even true of the variables declared in
the
main part of your program. (Since the call to main does not end until the program
T
HE

delete
O
PERATOR

The delete operator eliminates a dynamic variable and returns the memory that the dynamic
variable occupied to the freestore. The memory can then be reused to create new dynamic vari-
ables. For example, the following eliminates the dynamic variable pointed to by the pointer
variable
p:
delete p;
After a call to delete, the value of the pointer variable, like p above, is undefined. (A slightly
different version of
delete, discussed later in this chapter, is used when the dynamically allo-
cated variable is an array.)
dangling
pointer
Pointers 417
Tip
ends, the variables declared in main are not destroyed until the program ends, but the
mechanism for handling local variables is the same for
main as for any other function.)
These local variables are sometimes called automatic variables because their dynamic
properties are controlled automatically for you. They are automatically created when
the function in which they are declared is called and automatically destroyed when the
function call ends.
Variables declared outside any function or class definition, including outside
main,
are called global variables. These global variables are sometimes called statically allo-
cated variables, because they are truly static in contrast to dynamic and automatic
variables. We discussed global variables briefly in Chapter 3. As it turns out, we have no
need for global variables and have not used them.
4
D
EFINE

P
OINTER
T
YPES
You can define a pointer type name so that pointer variables can be declared like other variables
without the need to place an asterisk in front of each pointer variable. For example, the following
defines a type called
IntPtr, which is the type for pointer variables that contain pointers to int
variables:
typedef int* IntPtr;
Thus, the following two pointer variable declarations are equivalent:
IntPtr p;
and
int *p;
You can use typedef to define an alias for any type name or definition. For example, the follow-
ing defines the type name
Kilometers to mean the same thing as the type name double:
typedef double Kilometers;
Once you have given this type definition, you can define a variable of type double as follows:
Kilometers distance;
Renaming existing types this way can occasionally be useful. However, our main use of typedef
will be to define types for pointer variables.
Keep in mind that a typedef does not produce a new type but is simply an alias for the type
definition. For example, given the previous definition of
Kilometers, a variable of type
4
Variables declared within a class using the modifier static are static in a different sense than
the dynamic/static contrast we are discussing in this section.
automatic
variable

global
variable
typedef
418 Pointers and Dynamic Arrays
Self-Test Exercises
Kilometers may be substituted for a parameter of type double. Kilometers and double are
two names for the same type.
There are two advantages to using defined pointer type names, such as IntPtr defined previ-
ously. First, it avoids the mistake of omitting an asterisk. Remember, if you intend
p1 and p2 to
be pointers, then the following is a mistake:
int *p1, p2;
Since the * was omitted from the p2, the variable p2 is just an ordinary int variable, not a
pointer variable. If you get confused and place the
* on the int, the problem is the same but is
more difficult to notice. C++ allows you to place the
* on the type name, such as int, so that the
following is legal:
int* p1, p2;
Although the above is legal, it is misleading. It looks like both p1 and p2 are pointer variables,
but in fact only
p1 is a pointer variable; p2 is an ordinary int variable. As far as the C++ compiler
is concerned, the
* that is attached to the identifier int may as well be attached to the identifier
p1. One correct way to declare both p1 and p2 to be pointer variables is
int *p1, *p2;
An easier and less error-prone way to declare both p1 and p2 to be pointer variables is to use the
defined type name
IntPtr as follows:
IntPtr p1, p2;

The second advantage of using a defined pointer type, such as IntPtr, is seen when you define a
function with a call-by-reference parameter for a pointer variable. Without the defined pointer
type name, you would need to include both an
* and an & in the declaration for the function, and
the details can get confusing. If you use a type name for the pointer type, then a call-by-reference
parameter for a pointer type involves no complications. You define a call-by-reference parameter
for a defined pointer type just like you define any other call-by-reference parameter. Here’s an
example:
void sampleFunction(IntPtr& pointerVariable);
5. What unfortunate misinterpretation can occur with the following declaration?
int* intPtr1, intPtr2;
6. Suppose a dynamic variable were created as follows:
char *p;
p = new char;
Pointers 419
Pitfall
Assuming that the value of the pointer variable p has not changed (so it still points to the
same dynamic variable), how can you destroy this new dynamic variable and return the
memory it uses to the freestore manager so that the memory can be reused to create other
new dynamic variables?
7. Write a definition for a type called
NumberPtr that will be the type for pointer variables
that hold pointers to dynamic variables of type
double. Also, write a declaration for a
pointer variable called
myPoint, which is of type NumberPtr.
8. Describe the action of the
new operator. What does the new operator return? What are the
indications of errors?
P

OINTERS

AS
C
ALL
-
BY
-V
ALUE
P
ARAMETERS
When a call-by-value parameter is of a pointer type, its behavior can occasionally be subtle and
troublesome. Consider the function call shown in Display 10.4. The parameter
temp in the function
sneaky is a call-by-value parameter, and hence it is a local variable. When the function is called,
the value of
temp is set to the value of the argument p and the function body is executed. Since
temp is a local variable, no changes to temp should go outside the function sneaky. In particu-
lar, the value of the pointer variable
p should not be changed. Yet the sample dialogue makes it
look like the value of the pointer variable
p has changed. Before the call to the function sneaky,
the value of
*p was 77, and after the call to sneaky the value of *p is 99. What has happened?
T
YPE
D
EFINITIONS
You can assign a name to a type definition and then use the type name to declare variables. This
is done with the keyword

typedef. These type definitions are normally placed outside the body
of the
main part of your program and outside the body of other functions, typically near the start
of a file. That way the
typedef is global and available to your entire program. We will use type
definitions to define names for pointer types, as shown in the example below.
S
YNTAX
typedef
Known_Type_Definition

New_Type_Name
;
E
XAMPLE
typedef int* IntPtr;
The type name IntPtr can then be used to declare pointers to dynamic variables of type int, as
in the following example:
IntPtr pointer1, pointer2;
420 Pointers and Dynamic Arrays
The situation is diagrammed in Display 10.5. Although the sample dialogue may make it look as if
p were changed, the value of p was not changed by the function call to sneaky. Pointer p has two
things associated with it:
p’s pointer value and the value stored where p points. But the value of p
is the pointer (that is, a memory address). After the call to
sneaky, the variable p contains the
same pointer value (that is, the same memory address). The call to
sneaky has changed the value
of the variable pointed to by
p, but it has not changed the value of p itself.

If the parameter type is a class or structure type that has member variables of a pointer type, the
same kind of surprising changes can occur with call-by-value arguments of the class type. How-
ever, for class types, you can avoid (and control) these surprise changes by defining a
copy con-
structor
, as described later in this chapter.
Display 10.4 A Call-by-Value Pointer Parameter
(part 1 of 2)
1 //Program to demonstrate the way call-by-value parameters
2 //behave with pointer arguments.
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7 typedef int* IntPointer;
8 void sneaky(IntPointer temp);
9 int main(
)
10 {
11 IntPointer p;
12 p = new int;
13 *p = 77;
14 cout << "Before call to function *p == "
15 << *p << endl;
16 sneaky(p);
17 cout << "After call to function *p == "
18 << *p << endl;
19 return 0;
20 }
Pointers 421


USES FOR POINTERS
Chapter 17 discusses ways to use pointers to create a number of useful data structures.
This chapter only discusses one use of pointers, namely, to reference arrays, and in par-
ticular to create and reference a kind of array known as a dynamically allocated array.
Dynamically allocated arrays are the topic of Section 10.2.
Display 10.4 A Call-by-Value Pointer Parameter
(part 2 of 2)
21 void sneaky(IntPointer temp)
22 {
23 *temp = 99;
24 cout << "Inside function call *temp == "
25 << *temp << endl;
26 }
S
AMPLE
D
IALOGUE
Before call to function *p == 77
Inside function call *temp == 99
After call to function *p == 99
Display 10.5 The Function Call sneaky(p);

1. Before call to sneaky:
p
77
2. Value of p is plugged in for temp:
p
temp
77

4. After call to sneaky:3. Change made to *temp:
p
temp
99
p
99
422 Pointers and Dynamic Arrays
Dynamic Arrays
In this section you will see that array variables are actually pointer variables. You will
also find out how to write programs with dynamically allocated arrays. A dynamically
allocated array (also called simply a dynamic array) is an array whose size is not spec-
ified when you write the program, but is determined while the program is running.

ARRAY VARIABLES AND POINTER VARIABLES
Chapter 5 described how arrays are kept in memory. At that point we discussed arrays in
terms of memory addresses. But a memory address is a pointer. So, in C++ an array vari-
able is actually a kind of pointer variable that points to the first indexed variable of the
array. Given the following two variable declarations,
p and a are both pointer variables:
int a[10];
typedef int* IntPtr;
IntPtr p;
The fact that a and p are both pointer variables is illustrated in Display 10.6. Since a is
a pointer that points to a variable of type
int (namely, the variable a[0]), the value of a
can be assigned to the pointer variable p as follows:
p = a;
After this assignment, p points to the same memory location that a points to. Thus,
p[0], p[1], . . . p[9] refer to the indexed variables a[0], a[1], . . . a[9]. The square
bracket notation you have been using for arrays applies to pointer variables as long as

the pointer variable points to an array in memory. After the above assignment, you can
treat the identifier
p as if it were an array identifier. You can also treat the identifier a as
if it were a pointer variable, but there is one important reservation: You cannot change
the pointer value in an array variable. If the pointer variable
p2 has a value, you might be
tempted to think the following is legal, but it is not:
a = p2;//ILLEGAL. You cannot assign a different address to a.
The underlying reason why this assignment does not work is that an array variable is not
of type
int*, but its type is a const version of int*. An array variable, like a, is a pointer
variable with the modifier
const, which means that its value cannot be changed.
(An array variable is actually more than an ordinary pointer variable since it carries
some additional size information about the array, but an array variable does include a
pointer to the array and the array variable can be assigned to a pointer variable. So, an
array variable is a kind of pointer variable and can be treated like a pointer variable
whose value cannot be changed.)
10.2
dynamically
allocated
array
Dynamic Arrays 423

CREATING AND USING DYNAMIC ARRAYS
One problem with the kinds of arrays we discussed in Chapter 5 is that you must spec-
ify the size of the array when you write the program—but you may not know what size
array you need until the program is run. For example, an array might hold a list of stu-
dent identification numbers, but the size of the class may be different each time the
Display 10.6 Arrays and Pointer Variables

1 //Program to demonstrate that an array variable is a kind of pointer variable.
2 #include <iostream>
3 using std::cout;
4 using std::endl;
5 typedef int* IntPtr;
6 int main( )
7 {
8 IntPtr p;
9 int a[10];
10 int index;
11 for (index = 0; index < 10; index++)
12 a[index] = index;
13 p = a;
14 for (index = 0; index < 10; index++)
15 cout << p[index] << " ";
16 cout << endl;
17 for (index = 0; index < 10; index++)
18 p[index] = p[index] + 1;
19 for (index = 0; index < 10; index++)
20 cout << a[index] << " ";
21 cout << endl;
22 return 0;
23 }
S
AMPLE
D
IALOGUE
0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10
Note that changes

to the array
p
are also changes
to the array
a.
424 Pointers and Dynamic Arrays
program is run. With the kinds of arrays you have used thus far, you must estimate the
largest possible size you may need for the array and hope that size is large enough.
There are two problems with this. First, you may estimate too low, and then your pro-
gram will not work in all situations. Second, since the array might have many unused
positions, this can waste computer memory. Dynamically allocated arrays avoid these
problems. If your program uses a dynamically allocated array for student identification
numbers, then the number of students can be entered as input to the program and the
dynamically allocated array can be created to have a size exactly equal to the number of
students.
Dynamically allocated arrays are created using the
new operator. The creation and
use of dynamically allocated arrays is surprisingly simple. Since array variables are
pointer variables, you can use the
new operator to create dynamically allocated variables
that are arrays and can treat these dynamically allocated arrays as if they were ordinary
arrays. For example, the following creates a dynamically allocated array variable with
ten array elements of type
double:
typedef double* DoublePtr;
DoublePtr d;
d = new double[10];
To obtain a dynamically allocated array of elements of any other type, simply replace
double with the desired type. In particular, you can replace the type double with a
struct or class type. To obtain a dynamically allocated array variable of any other size,

simply replace
10 with the desired size.
There are also a number of less-obvious things to notice about this example. First,
the pointer type that you use for a pointer to a dynamically allocated array is the same
as the pointer type you would use for a single element of the array. For instance, the
pointer type for an array of elements of type
double is the same as the pointer type you
would use for a simple variable of type
double. The pointer to the array is actually a
pointer to the first indexed variable of the array. In the above example, an entire array
with ten indexed variables is created, and the pointer
p is left pointing to the first of
these ten indexed variables.
Second, notice that when you call
new, the size of the dynamically allocated array is
given in square brackets after the type, which in this example is the type
double. This
tells the computer how much storage to reserve for the dynamic array. If you omitted
the square brackets and the
10 in this example, the computer would allocate enough
storage for only one variable of type
double, rather than for an array of ten indexed
variables of type
double.
Display 10.7 contains a program that illustrates the use of a dynamically allocated
array. The program searches a list of numbers stored in a dynamically allocated array.
The size of the array is determined when the program is run. The user is asked how
many numbers there will be, and then the
new operator creates a dynamically allocated
array of that size. The size of the dynamic array is given by the variable

arraySize. The
size of a dynamic array need not be given by a constant. It can, as in Display 10.7, be
given by a variable whose value is determined when the program is run.
creating a
dynamic array

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×