The Pointer Data Type and Pointer Variables
The values belonging to pointer data types are the memory addresses of your computer.
However, there is no name associated with the pointer data type in C++. Because the
domain, (that is, the values of a pointer data type), consists of addresses (memory
locations), a pointer variable is a variable whose content is an address, that is, a memory
location.
Pointer variable: A variable whose content is an address (that is, a memory address).
Declaring Pointer Variables
The value of a pointer variable is an address. That is, the value refers to another memory
space. The data is typically stored in this memory space. Therefore, when you declare a
pointer variable, you also specify the data type of the value to be stored in the memory
location to which the pointer variable points.
In C++, you declare a pointer variable by using the asterisk symbol ( *) between the
data type and the variable name. The general syntax to declare a pointer variable is as
follows:
dataType *identifier;
As an example, consider the following statements:
int *p;
char *ch;
In these statements, both p and ch are pointer variables. The content of p (when properly
assigned) points to a memory location of type int, and the content of ch points to a
memory location of type char. Usually p is called a pointer variable of type int, and ch
is called a pointer variable of type char.
Before discussing how pointers work, let us make the following observations. The
following statements that declare p to be a pointer variable of type int are
equivalent:
int *p;
int* p;
int * p;
Thus, the character * can appear anywhere between the data type name and the variable
name.
Now consider the following statement:
int* p, q;
In this statement, only p is a pointer variable, not q. Here q is an int variable. To avoid
confusion, we prefer to attach the character * to the variable name. So the preceding
statement is written as follows:
int *p, q;
Of course, the statement
int *p, *q;
declares both p and q to be pointer variables of type int.
Now that you know how to declare pointers, next we discuss how to make a pointer
point to a memory space and how to manipulate the data stored in these memory
locations.
Because the value of a pointer is a memory address, a pointer can store the address of a
memory space of the designated type. For example, if p is a pointer of type int, p can
store the address of any memory space of type int. C++ provides two operators—the
address of operator (&) and the dereferencing operator ( *)—to work with pointers. The
next two sections describe these operators.
Address of Operator (&)
In C++, the ampersand, &, called the address of operator, is a unary operator that
returns the address of its operand. For example, given the statements
int x;
int *p;
the statement
p = &x;
assigns the address of x to p. That is, x and the value of p refer to the same memory
location.
Dereferencing Operator (*)
The previous chapters used the asterisk character, *, as the binary multiplication operator.
C++ also uses * as a unary operator. When *, commonly referred to as the dereferencing operator or indirection operator, is used as a unary operator, * refers to the
3
The Pointer Data Type and Pointer Variables | 133
object to which the operand of the * (that is, the pointer) points. For example, given the
statements
int x = 25;
int *p;
p = &x; //store the address of x in p
the statement
cout << *p << endl;
prints the value stored in the memory space to which p points, which is the value of x.
Also, the statement
*p = 55;
stores 55 in the memory location to which p points—that is, 55 is stored in x.
Pointers and Classes
Consider the following statements:
string *str;
str = new string;
*str = “Sunny Day”;
The first statement declares str to be a pointer variable of type string. The second
statement allocates memory of type string and stores the address of the allocated memory
in str. The third statement stores the string “Sunny Day” in the memory to which str
points. Now suppose that you want to use the string function length to find the length of
the string “Sunny Day”. The statement (*str).length() returns the length of the string.
Note the parentheses around *str. The expression (*str).length() is a mixture of
pointer dereferencing and the class component selection. In C++, the dot operator, ., has a
higher precedence than the dereferencing operator, *. Let us elaborate on this a little more.
In the expression (*str).length(), the operator * evaluates first, so the expression *str
evaluates first. Because str is a pointer variable of type string, *str refers to a memory
space of type string. Therefore, in the expression (*str).length(), the function
length of the class string executes. Now consider the expression *str.length().
Let us see how this expression gets evaluated. Because . has a higher precedence than *, the
expression str.length() evaluates first. The expression str.length() would result in a
syntax error because str is not a string object, so it cannot use the function length of
the class string.
As you can see, in the expression (*str).length(), the parentheses around *str are
important. However, typos are unavoidable. Therefore, to simplify the accessing of
class or struct components via a pointer, C++ provides another operator, called
the member access operator arrow, ->. The operator -> consists of two consecutive
symbols: a hyphen and the ‘‘greater than’’ symbol.
The syntax for accessing a class (struct) member using the operator -> is as follows:
pointerVariableName->classMemberName
Thus, the expression
(*str).length()
is equivalent to the expression
str->length()
Accessing class (struct) components via pointers using the operator -> thus eliminates
the use both of the parentheses and of the dereferencing operator. Because typos are
unavoidable and missing parentheses can result in either an abnormal program termination
or erroneous results, when accessing class (struct) components via pointers, this book
uses the arrow notation.
Initializing Pointer Variables
Because C++ does not automatically initialize variables, pointer variables must be
initialized if you do not want them to point to anything. Pointer variables are initialized
using the constant value 0, called the null pointer. Thus, the statement p = 0; stores the
null pointer in p; that is, p points to nothing. Some programmers use the named constant
NULL to initialize pointer variables. The following two statements are equivalent:
p = NULL;
p = 0;
The number 0 is the only number that can be directly assigned to a pointer variable.
Dynamic Variables
In the previous sections, you learned how to declare pointer variables, how to store the
address of a variable into a pointer variable of the same type as the variable, and how to
manipulate data using pointers. However, you learned how to use pointers to manipulate
data only into memory spaces that were created using other variables. In other words, the
pointers manipulated data into existing memory spaces. So what is the benefit to using
pointers? You can access these memory spaces by working with the variables that were
used to create them. In this section, you learn about the power behind pointers. In
particular, you learn how to allocate and deallocate memory during program execution
using pointers.
Variables that are created during program execution are called dynamic variables. With
the help of pointers, C++ creates dynamic variables. C++ provides two operators, new
and delete, to create and destroy dynamic variables, respectively. When a program
requires a new variable, the operator new is used. When a program no longer needs a
dynamic variable, the operator delete is used.
In C++, new and delete are reserved words.
Operator new
The operator new has two forms: one to allocate a single variable, and another to allocate
an array of variables. The syntax to use the operator new is as follows:
new dataType; //to allocate a single variable
new dataType[intExp]; //to allocate an array of variables
where intExp is any expression evaluating to a positive integer.
The operator new allocates memory (a variable) of the designated type and returns a
pointer to it—that is, the address of this allocated memory. Moreover, the allocated
memory is uninitialized.
Consider the following declaration:
int *p;
char *q;
int x;
The statement
p = &x;
stores the address of x in p. However, no new memory is allocated. On the other hand,
consider the following statement:
p = new int;
This statement creates a variable during program execution somewhere in memory, and
stores the address of the allocated memory in p. The allocated memory is accessed via
pointer dereferencing—namely, *p. Similarly, the statement
q = new char[16];
creates an array of 16 components of type char and stores the base address of the array in q.
Because a dynamic variable is unnamed, it cannot be accessed directly. It is accessed
indirectly by the pointer returned by new. The following statements illustrate this concept:
int *p; //p is a pointer of type int
p = new int; //allocates memory of type int and stores the address
//of the allocated memory in p
*p = 28; //stores 28 in the allocated memory
Operations on Pointer Variables
The operations that are allowed on pointer variables are the assignment and relational
operations and some limited arithmetic operations. The value of one pointer variable can
be assigned to another pointer variable of the same type. Two pointer variables of the
same type can be compared for equality, and so on. Integer values can be added and
subtracted from a pointer variable. The value of one pointer variable can be subtracted
from another pointer variable.
For example, suppose that we have the following statements:
int *p, *q;
The statement
p = q;
copies the value of q into p. After this statement executes, both p and q point to the same
memory location. Any changes made to *p automatically change the value of *q, and vice versa.
The expression
p == q
evaluates to true if p and q have the same value—that is, if they point to the same
memory location. Similarly, the expression
p != q
evaluates to true if p and q point to different memory locations.
The arithmetic operations that are allowed differ from the arithmetic operations on
numbers. First, let us use the following statements to explain the increment and decrement operations on pointer variables:
int *p;
double *q;
char *chPtr;
Suppose that the size of the memory allocated for an int variable is 4 bytes, a double
variable is 8 bytes, and a char variable is 1 byte.
The statement
p++; or p = p + 1;
increments the value of p by 4 bytes because p is a pointer of type int. Similarly, the statements
q++;
chPtr++;
increment the value of q by 8 bytes and the value of chPtr by 1 byte, respectively.
The increment operator increments the value of a pointer variable by the size of the
memory to which it is pointing. Similarly, the decrement operator decrements the value
of a pointer variable by the size of the memory to which it is pointing.
Moreover, the statement
p = p + 2;
increments the value of p by 8 bytes.
Dynamic Arrays
The arrays used earlier are called static arrays because their size was fixed at compile time. One
of the limitations of a static array is that every time you execute the program, the size of
the array is fixed, so it might not be possible to use the same array to process different data sets of
the same type. One way to handle this limitation is to declare an array that is large enough to
process a variety of data sets. However, if the array is big and the data set is small, such a
declaration would result in memory waste. On the other hand, it would be helpful if, during
program execution, you could prompt the user to enter the size of the array and then create an
array of the appropriate size. This approach is especially helpful if you cannot even guess the
array size. In this section, you learn how to create arrays during program execution and how to
process such arrays.
An array created during the execution of a program is called a dynamic array. To create
a dynamic array, we use the second form of the new operator.
The statement
int *p;
declares p to be a pointer variable of type int. The statement
p = new int[10];
allocates 10 contiguous memory locations, each of type int, and stores the address of the
first memory location into p. In other words, the operator new creates an array of 10
components of type int, it returns the base address of the array, and the assignment
operator stores the base address of the array into p. Thus, the statement
*p = 25;
stores 25 into the first memory location, and the statements
p++; //p points to the next array component
*p = 35;
store 35 into the second memory location. Thus, by using the increment and decrement
operations, you can access the components of the array. Of course, after performing a few
increment operations, it is possible to lose track of the first array component. C++ allows
us to use array notation to access these memory locations. For example, the statements
p[0] = 25;
p[1] = 35;
store 25 and 35 into the first and second array components, respectively. That is, p[0]
refers to the first array component, p[1] refers to the second array component, and so on.
In general, p[i] refers to the (i + 1)th array component. After the preceding statements
execute, p still points to the first array component.
The following for loop initializes each array component to 0:
for (int j = 0; j < 10; j++)
p[j] = 0;
The Pointer Data Type and Pointer Variables | 147
When the array notation is used to process the array to which p points, p stays fixed at
the first memory location. Note that p is a dynamic array, created during program
execution.
IMPORTANT POINTS FOR POINTERS:(QUICK OVERVIEW OF ABOVE DISCUSION)
1. Pointer variables contain the addresses of other variables as their values.
2. In C++, no name is associated with the pointer data type.
3. A pointer variable is declared using an asterisk, *, between the data type and
the variable.
cin >> p; //Line 9
cout << endl << “Line 10: p(x): ” << p << endl; //Line 10
cout << “Line 11: p(5): ” << p(5) << endl << endl; //Line 11
cin >> q; //Line 12
cout << endl << “Line 13: q(x): ” << q << endl
<< endl; //Line 13
t = p + q; //Line 14
cout << “Line 15: p(x) + q(x): ” << t << endl; //Line 15
cout << “Line 16: p(x) – q(x): ” << p – q << endl; //Line 16
return 0; //Line 17
} //Line 18
Sample Run: In this sample run, the user input is shaded.
The degree of this polynomial is: 7
Enter the coefficient of x^0: 0
Enter the coefficient of x^1: 1
Enter the coefficient of x^2: 4
Enter the coefficient of x^3: 0
Enter the coefficient of x^4: 0
Enter the coefficient of x^5: 0
Enter the coefficient of x^6: 0
Enter the coefficient of x^7: 6
Line 10: p(x): 1x^1 + 4x^2 + 6x^7
Line 11: p(5): 468855
The degree of this polynomial is: 3
Enter the coefficient of x^0: 1
Enter the coefficient of x^1: 2
Enter the coefficient of x^2: 0
Enter the coefficient of x^3: 3
Line 13: q(x): 1 + 2x^1 + 3x^3
Line 15: p(x) + q(x): 1 + 3x^1 + 4x^2 + 3x^3 + 6x^7
Line 16: p(x) – q(x): -1 – 1x^1 + 4x^2 – 3x^3 + 6x^7
4. In C++, & is called the address of operator.
5. The address of operator returns the address of its operand. For example, if p
is a pointer variable of type int and num is an int variable, the statement
p = #
sets the value of p to the address of num.
6. When used as a unary operator, * is called the dereferencing operator.
7. The memory location indicated by the value of a pointer variable is accessed
by using the dereferencing operator, *. For example, if p is a pointer variable
of type int, the statement
*p = 25;
sets the value of the memory location indicated by the value of p to 25.
8. You can use the member access operator arrow, ->, to access the component of an object pointed to by a pointer.
9. Pointer variables are initialized using either 0 (the integer zero), NULL, or
the address of a variable of the same type.
10. The only integer value that can be directly assigned to a pointer variable is 0.
11. The only arithmetic operations allowed on pointer variables are increment (++),
decrement (–), addition of an integer to a pointer variable, subtraction of an
integer from a pointer variable, and subtraction of a pointer from another pointer.
12. Pointer arithmetic is different from ordinary arithmetic. When an integer is
added to a pointer, the value added to the value of the pointer variable is
the integer times the size of the object to which the pointer is pointing.
Similarly, when an integer is subtracted from a pointer, the value subtracted
from the value of the pointer variable is the integer times the size of the
object to which the pointer is pointing.
13. Pointer variables can be compared using relational operators. (It makes
sense to compare pointers of the same type.
14. The value of one pointer variable can be assigned to another pointer
variable of the same type.
15. A variable created during program execution is called a dynamic variable
16. The operator new is used to create a dynamic variable.
17. The operator delete is used to deallocate the memory occupied by a
dynamic variable.
18. In C++, both new and delete are reserved words.
19. The operator new has two forms: one to create a single dynamic variable,
and another to create an array of dynamic variables.
20. If p is a pointer of type int, the statement
p = new int;
allocates storage of type int somewhere in memory and stores the address
of the allocated storage in p.
21. The operator delete has two forms: one to deallocate the memory
occupied by a single dynamic variable, and another to deallocate the
memory occupied by an array of dynamic variables.
22. If p is a pointer of type int, the statement delete p; deallocates the
memory to which p points.
23. The array name is a constant pointer. It always points to the same memory
location, which is the location of the first array component.
24. To create a dynamic array, the form of the new operator that creates an
array of dynamic variables is used. For example, if p is a pointer of type
int, the statement
p = new int[10];
creates an array of 10 components of type int. The base address of the array
is stored in p. We call p a dynamic array.
25. Array notation can be used to access the components of a dynamic array. For
example, suppose p is a dynamic array of 10 components. Then p[0] refers
to the first array component, p[1] refers to the second array component,
and so on. In particular, p[i] refers to the (i + 1)th component of the array.
26. An array created during program execution is called a dynamic array.
27. If p is a dynamic array, then the statement
delete [] p;