Pointers - 图1Pointers

C++ reveals its true power through pointer variables. Pointer vari- ables (or pointers, as they generally are called) are variables that contain addresses of other variables. All variables you have seen so far have held data values. You understand that variables hold various data types: character, integer, floating-point, and so on. Pointer variables contain the location of regular data variables; they in effect point to the data because they hold the address of the data. When first learning C++, students of the language tend to shy away from pointers, thinking that pointers will be difficult. Pointers do not have to be difficult. In fact, after you work with them for a while, you will find they are easier to use than arrays (and much

more flexible).

This chapter introduces the following concepts:

  • Pointers

  • Pointers of different data types

  • The “address of” (&) operator

  • The dereferencing (* ) operator

  • Arrays of pointers

Pointers offer a highly efficient means of accessing and chang- ing data. Because pointers contain the actual address of your data, your compiler has less work to do when finding that data in memory. Pointers do not have to link data to specific variable names. A pointer can point to an unnamed data value. With pointers, you gain a “different view” of your data.

Pointers contain addresses of other variables.

Introduction to Pointe r Variables

Pointers are variables. They follow all the normal naming rules of regular, nonpointer variables. As with regular variables, you must declare pointer variables before using them. There is a type of pointer for every data type in C++; there are integer pointers, character pointers, floating-point pointers, and so on. You can declare global pointers or local pointers, depending on where you declare them.

About the only difference between pointer variables and regu- lar variables is the data they hold. Pointers do not contain data in the usual sense of the word. Pointers contain addresses of data. If you need a quick review of addresses and memory, see Appendix A, “Memory Addressing, Binary, and Hexadecimal Review.”

There are two pointer operators in C++:

& The “address of” operator

* The dereferencing operator

Don’t let these operators throw you; you might have seen them before! The & is the bitwise AND operator (from Chapter 11, “Addi- tional C++ Operators”) and the * means, of course, multiplication. These are called overloaded operators. They perform more than one function, depending on how you use them in your programs. C++ does not confuse * for multiplication when you use it as a dereferencing operator with pointers.

Any time you see the & used with pointers, think of the words “address of.” The & operator always produces the memory address of whatever it precedes. The * operator, when used with pointers, either declares a pointer or dereferences the pointer’s value. The next section explains each of these operators.

Pointers - 图2Declaring Pointer s

Because you must declare all pointers before using them, the best way to begin learning about pointers is to understand how to declare and define them. Actually, declaring pointers is almost as easy as declaring regular variables. After all, pointers are variables.

If you must declare a variable that holds your age, you could do so with the following variable declaration:

i nt age=30; / / Decl ar e a var i abl e t o hol d my age.

Declaring age like this does several things. It enables C++ to identify a variable called age, and to reserve storage for that variable. Using this format also enables C++ to recognize that you will store only integers in age, not floating-point or double floating-point data. The declaration also requests that C++ store the value of 30 in age after it reserves storage for age.

Pointers - 图3Where did C++ store age in memory? As the programmer, you should not really care where C++ stores age. You do not have to know the variable’s address because you will never refer to age by its address. If you want to calculate with or print age, you call it by its name, age.

Suppose you want to declare a pointer variable. This pointer variable will not hold your age, but it will point to age, the variable that holds your age. (Why you would want to do this is explained in this and the next few chapters.) p_age might be a good name for the pointer variable. Figure 26.1 illustrates what you want to do. The

figure assumes C++ stored age at the address 350,606. Your C++ compiler, however, arbitrarily determines the address of age, so it could be anything.

Pointers - 图4

Figure 26.1. p_age contains the address of age; p_age points to the age

variable.

The name p_age has nothing to do with pointers, except that it is the name you made up for the pointer to age. Just as you can name variables anything (as long as the name follows the legal naming rules of variables), p_age could just as easily have been named house , x43344 , space_t r ek , or whatever else you wanted to call it. This reinforces the idea that a pointer is just a variable you reserve in your program. Create meaningful variable names, even for pointer vari- ables. p_age is a good name for a variable that points to age (as would be pt r _age and pt r _t o_age ).

To declare the p_age pointer variable, you must program the following:

i nt * p_age; / / Decl ar es an i nt eger poi nt er .

Similar to the declaration for age, this declaration reserves a variable called p_age . The p_age variable is not a normal integer variable, however. Because of the dereferencing operator, * , C++ knows this is to be a pointer variable. Some C++ programmers prefer to declare such a variable without a space after the * , as follows:

i nt * p_age; / / Decl ar es an i nt eger poi nt er .

Pointers - 图5Pointers - 图6Either method is okay, but you must remember the * is not part of the name. When you later use p_age , you will not prefix the name with the * , unless you are dereferencing it at the time (as later examples show).

Consider the declaration for p_age if the asterisk were not there: C++ would think you were declaring a regular integer variable. The

* is important, because it tells C++ to interpret p_age as a pointer variable, not as a normal, data variable.

Pointers can point

Assigning Values to Pointer s

p_age is an integer pointer. This is very important. p_age can

only to data of their own type.

point only to integer values, never to floating-point, double floating- point, or even character variables. If you needed to point to a floating-point variable, you might do so with a pointer declared as

f l oat * poi nt ; / / Decl ar es a f l oat i ng- poi nt poi nt er .

As with any automatic variable, C++ does not initialize point- ers when you declare them. If you declared p_age as previously described, and you wanted p_age to point to age, you would have to explicitly assign p_age to the address of age. The following statement does this:

p_age = &age; / / Assi gn t he addr ess of age t o p_age.

What value is now in p_age ? You do not know exactly, but you know it is the address of age, wherever that is. Rather than assign the address of age to p_age with an assignment operator, you can declare and initialize pointers at the same time. These lines declare and initialize both age and p_age :

i nt age=30; / / Decl ar es a r egul ar i nt eger

/ / var i abl e, putt i ng 30 i n i t.

i nt * p_age=&age; / / Decl ar es an i nt eger poi nt er ,

/ / i ni t i ali zi ng i t wi t h t he addr ess

/ / of p_age.

These two lines produce the variables described in Figure 26.1.

If you wanted to print the value of age, you could do so with the following cout :

cout << age; / / Pr i nt s t he val ue of age.

Pointers - 图7You also can print the value of age like this:

cout << * p_age; / / Der ef er ences p_age.

The dereference operator produces a value that tells the pointer where to point. Without the * , the last cout would print an address (the address of age). With the * , the cout prints the value at that address.

You can assign a different value to age with the following statement:

age=41; / / Assi gns a new val ue t o age.

You also can assign a value to age like this:

* p_age=41;

Pointers - 图8This declaration assigns 41 to the value to which p_age points.

Pointers and Parameter s

Now that you understand the pointer’s * and & operators, you can finally see why scanf () ’s requirements were not as strict as they first seemed. While passing a regular variable to scanf () , you had to prefix the variable with the & operator. For instance, the following scanf () gets three integer values from the user:

scanf (“ %d %d %d” , &num1, &num2, &num3) ;

This scanf () does not pass the three variables, but passes the addresses of the three variables. Because scanf () knows the exact locations of these parameters in memory (because their addresses were passed), it goes to those addresses and puts the keyboard input values into those addresses.

Pointers - 图9This is the only way scanf () could work. If you passed these variables by copy, without putting the “address of” operator (&) before them, scanf () would get the keyboard input and fill a copy of the variables, but not the actual variables num1, num2, and num3. When scanf () then returned control to your program, you would not have the input values. Of course, the ci n operator does not have the ampersand (&) requirement and is easier to use for most C++ programs.

You might recall from Chapter 18, “Passing Values,” that you can override C++’s normal default of passing by copy (or “by value”). To pass by address, receive the variable preceded by an & in the receiving function. The following function receives t r i es by address:

pr _i t ( i nt &t r i es) ; / / Recei ve i nt eger t r i es i n pr _i t () by

/ / addr ess ( pr i t woul d normall y r ecei ve

/ / t r i es by copy) .

Now that you understand the & and * operators, you can understand completely the passing of nonarray parameters by address to functions. (Arrays default to passing by address without requiring that you use &.)

Examples

  1. Pointers - 图10The

    following section of code declares three regular vari- ables of three different data types, and three corresponding pointer variables:

char i ni t i al = ‘ Q’ ; / / Decl ar es t hr ee r egul ar var i abl es i nt num=40; / / of t hr ee di ff er ent t ypes.

f l oat sal es=2321. 59;

char * p_i ni t i al =&i ni t i al ; / / Decl ar es t hr ee poi nt er s.

Pointers - 图11i nt * pt r _num=# / / Poi nt er names and spaci ng f l oat * sal es_add = &sal es; / / aft er * ar e not cr i t i cal .

  1. Just like regular variables, you can initialize pointers with

    assignment statements. You do not have to initialize them when you declare them. The next few lines of code are equivalent to the code in Example 1:

char i ni t i al ; / / Decl ar es t hr ee r egul ar var i abl es i nt num; / / of t hr ee di ff er ent t ypes.

f l oat sal es;

char * p_i ni t i al ; / / Decl ar es t hr ee poi nt er s but does i nt * pt r _num; / / not i ni t i ali ze t hem yet.

f l oat * sal es_add;

i ni t i al =’ Q’ ; / / I ni t i ali zes t he r egul ar var i abl es num=40; / / wi t h val ues.

sal es=2321. 59;

p_i ni t i al =&i ni t i al ; / / I ni t i ali zes t he poi nt er s wi t h pt r _num=# / / t he addr esses of t hei r sal es_add=&sal es; / / corr espondi ng var i abl es.

Notice that you do not put the * operator before the pointer variable names when assigning them values. You would prefix a pointer variable with the * only if you were dereferencing it.

Pointers - 图12

Keep the data type of each pointer consistent with its corre- sponding variable. Do not assign a floating-point variable to an integer’s address. For instance, you cannot make the following assignment statement:

p_i ni t i al = &sal es; / / I nvali d poi nt er assi gnment.

Pointers - 图13because p_i ni t i al can point only to character data, not to floating-point data.

  1. Pointers - 图14The

    following program is an example you should study closely. It shows more about pointers and the pointer opera- tors, & and * , than several pages of text can do.

/ / Fil ename: C26POI NT. CPP

/ / Demonst r at es t he use of poi nt er decl ar at i ons

/ / and oper at or s.

#i ncl ude <i ost r eam. h>

voi d mai n()

{

i nt num=123; / / A r egul ar i nt eger var i abl e.

i nt * p_num; / / Decl ar es an i nt eger poi nt er .

cout << “ num i s “ << num << “ \ n” ; / / Pr i nt s val ue of num. cout << “ The addr ess of num i s “ << &num << “ \ n” ;

/ / Pr i nt s num’ s l ocat i on.

p_num = # / / Put s addr ess of num i n p_num,

/ / i n eff ect maki ng p_num poi nt

/ / t o num.

/ / No * i n f r ont of p_num. cout << “ * p_num i s “ << * p_num << “ \ n” ; / / Pr i nt s val ue

/ / of num.

cout << “ p_num i s “ << p_num << “ \ n” ; / / Pr i nt s l ocat i on

/ / of num.

r et ur n;

}

Here is the output from this program:

num i s 123

The addr ess of num i s 0x8f bd0ff e

* p_num i s 123 p_num i s 0x8f bd0ff e

If you run this program, you probably will get different results for the value of p_num because your compiler will place num at a different location, depending on your memory setup. The value of p_num prints in hexadecimal because it is an address of memory. The actual address does not matter, however. Because the pointer p_num always contains the address of num, and because you can dereference p_num to get num’s value, the actual address is not critical.

  1. The following program includes a function that swaps the values of

    any two integers passed to it. You might recall that a function can return only a single value. Therefore, before now, you could not write a function that changed two different values and returned both values to the calling function.

To swap two variables (reversing their values for sorting, as you saw in Chapter 24, “Array Processing”), you need the ability to pass both variables by address. Then, when the function reverses the variables, the calling function’s vari- ables also are swapped.

Notice the function’s use of dereferencing operators before each occurrence of num1 and num2. It does not matter at which address num1 and num2 are stored, but you must make sure that you dereference whatever addresses were passed to the function.

Be sure to receive arguments with the prefix & in functions that receive by address, as done here.

Pointers - 图15Pointers - 图16Identify the program and include the I/O header file. This program swaps two integers, so initialize two integer variables in mai n() . Pass the variables to the swapping function, called swap_t hem*, then switch their values. Print the results of the swap in* mai n() *.*

/ / Fil ename: C26SWAP. CPP

/ / Pr ogr am t hat i ncl udes a f unct i on t hat swaps

Pointers - 图17/ / any t wo i nt eger s passed t o i t #i ncl ude <i ost r eam. h>

voi d swap_t hem( i nt &num1, i nt &num2) ;

voi d mai n()

{

i nt i =10, j =20;

cout << “ \ n\ nBef or e swap, i i s “ << i << “ and j i s “ << j << “ \ n\ n” ;

swap_t hem( i , j ) ;

cout << “ \ n\ nAft er swap, i i s “ << i << “ and j i s “ << j << “ \ n\ n” ;

r et ur n;

}

voi d swap_t hem( i nt &num1, i nt &num2)

{

i nt t emp; / / Var i abl e t hat hol ds

/ / i n- bet ween swapped val ue.

t emp = num1; / / The calli ng f unct i on’ s var i abl es num1 = num2; / / ( and not copi es of t hem) ar e num2 = t emp; / / changed i n t hi s f unct i on.

r et ur n;

}

Arrays of Pointer s

If you have to reserve many pointers for many different values, you might want to declare an array of pointers. You know that you can reserve an array of characters, integers, long integers, and floating-point values, as well as an array of every other data type available. You also can reserve an array of pointers, with each pointer being a pointer to a specific data type.

The following reserves an array of 10 integer pointer variables:

i nt * i pt r [ 10] ; / / Reser ves an arr ay of 10 i nt eger poi nt er s

Figure 26.2 shows how C++ views this array. Each element holds an address (after being assigned values) that points to other values in memory. Each value pointed to must be an integer. You can assign an element from i pt r an address just as you would for nonarray pointer variables. You can make i pt r [ 4] point to the address of an integer variable named age by assigning it like this:

i pt r [ 4] = &age; / / Make i pt r [ 4] poi nt t o addr ess of age.

Pointers - 图18

Figure 26.2. An array of 10 integer pointers.

The following reserves an array of 20 character pointer variables:

char * cpoi nt[ 20] ; / / Arr ay of 20 char act er poi nt er s.

Again, the asterisk is not part of the array name. The asterisk lets C++ know that this is an array of integer pointers and not just an array of integers.

Some beginning C++ students get confused when they see such a declaration. Pointers are one thing, but reserving storage for arrays of pointers tends to bog novices down. However, reserving storage for arrays of pointers is easy to understand. Remove the asterisk from the previous declaration as follows,

char cpoi nt[ 20];

Pointers - 图19and what do you have? You have just reserved a simple array of 20 characters. Adding the asterisk tells C++ to go one step further: rather than an array of character variables, you want an array of character pointing variables. Rather than having each element be a character variable, you have each element hold an address that points to characters.

Reserving arrays of pointers will be much more meaningful after you learn about structures in the next few chapters. As with regular, nonpointing variables, an array makes processing several pointer variables much easier. You can use a subscript to reference each variable (element) without having to use a different variable name for each value.

Review Question s

Answers to review questions are in Appendix B.

  1. Pointers - 图20What

    type of variable is reserved in each of the following?

    1. i nt * a;

    2. char * cp;

    3. f l oat * dp;

  2. What words should come to mind when you see the &

operator?

  1. What is the dereferencing operator?

  2. Pointers - 图21How

    would you assign the address of the floating-point variable sal ar y to a pointer called pt _sal ?

  3. True or false: You must define a pointer with an initial value when

    declaring it.

  4. In both of the following sections of code:

i nt i ;

i nt * pt i ; i =56;

pt i = &i ;

and

i nt i ;

i nt * pt i ;

pt i = &i ; / / These t wo li nes ar e r ever sed

i =56; / / f r om t he pr ecedi ng exampl e.

is the value of pt i the same after the fourth line of each section?

  1. Pointers - 图22In

    the following section of code:

f l oat pay;

f l oat * pt r _pay; pay=2313. 54;

pt r _pay = &pay;

What is the value of each of the following (answer “invalid” if it cannot be determined):

  1. pay

  2. * pt r_pay

  3. * pay

  4. &pay

  1. What does the following declare?

doubl e * ar a[ 4][ 6];

  1. An array of double floating-point values

  2. An array of double floating-point pointer variables

  3. An invalid declaration statement

Pointers - 图23

Pointers - 图24

Summary

Declaring and using pointers might seem troublesome at this point. Why assign * p_num a value when it is easier (and clearer) to assign a value directly to num? If you are asking yourself that question, you probably understand everything you should from this chapter and are ready to begin learning the true power of pointers: combining pointers and array processing.

Pointers - 图25