You are on page 1of 20

Lecture 13

Pointers
Introduction
• Computer memory can be imagined as a very large array of bytes. Every byte in
the computer’s memory has an address.
• As an array, these bytes are indexed from 0. The index of each byte is its
memory address.
• Any program loaded into memory, occupies a certain range of these addresses.
That means every variable and every function in your program starts at a
particular address.
• A variable declaration associates three fundamental attributes to the variable:
its name, its type, and its memory address. For example, the declaration int n;
associates the name n, the type int, and the address of some location in
memory where the value of n is stored.
• Suppose that address is 0x0064fdf0. Then we can visualize n like this:
• The variable’s value 44 is stored in the four bytes allocated to it
Introduction
• You can find the address occupied by a variable by using the address-of operator &.
• cout << &var1 << endl //prints the address of variable var
• We can also store the address in a variable that also holds an address value and is
called a pointer variable, or simply a pointer.
• Pointers enable pass-by-reference and can be used to create and manipulate
dynamic data structures that can grow and shrink, such as linked lists, queues,
stacks and trees.
• Normally, a variable directly contains a specific value.
• A pointer contains the memory address of a variable that, in turn, contains a
specific value. In this sense, a variable name directly references a value, and a
pointer indirectly references a value.
• Referencing a value through a pointer is called indirection.
• Pointers, like any other variables, must be declared before they can be used.
Introduction
• For example, for the pointer var, the declaration
• int* var;
declares the variable var to be of type int* (i.e., a pointer to an int value) and
is read (right to left), “var is a pointer to int”.
• Each variable being declared as a pointer must be preceded by an asterisk ( * ).
When * appears in a declaration, it’s not an operator; rather, it indicates that
the variable being declared is a pointer. Pointers can be declared to point to
objects of any data type. * is called dereference or indirection operator.
• If you define more than one pointer of the same type on one line, you need to
place an asterisk before each variable name.
With or without space
• char* ptr1, * ptr2, * ptr3; // three variables of type char* between * and
• Or you can use the asterisk-next-to-the-name approach. variable name

• char *ptr1, *ptr2, *ptr3; // three variables of type char*


Introduction
• Using the Address ( & ) and Indirection ( * ) Operators
Introduction
• Here are some common uses of Pointers:
• Accessing array elements
• Passing arguments to a function when the function needs to modify the original argument
• Passing arrays and strings to functions
• Obtaining memory from the system
• Creating data structures such as linked lists
Introduction
• Pointers should be initialized to nullptr or to a memory address either when
they’re declared or in an assignment.
• A pointer with the value nullptr “points to nothing” and is known as a null
pointer.

• Suppose that we don’t know the name of a variable but we do know its address.
Can we access the contents of the variable?
Introduction
• Yes, we can:
#include <iostream>
using namespace std;
int main()
{
int var1 = 11; //two integer variables
int var2 = 22;
int* ptr; //pointer to integers
ptr = &var1; //pointer points to var1
cout << *ptr << endl; //print contents of pointer (11)
ptr = &var2; //pointer points to var2
cout << *ptr << endl; //print contents of pointer (22)
return 0;
}
Introduction
• Purposes of unary operators &:
1. When applied as a prefix to the name of an object, it forms an expression that evaluates
to the address of the object. For example, ptr = &var1;
2. When applied as a suffix to a type T, it names the derived type “reference to T”. For
example, int& is the type “reference to int”.
• Purposes of unary operators *:
1. When applied as a prefix to a pointer to an object, it forms an expression that evaluates
to that object’s value. For example, * ptr = &var1;
2. When applied as a suffix to a type T, it names the derived type “pointer to T”. For
example, int* is the type “pointer to int”.
Introduction
• You can use a pointer not only to display a variable’s value, but also to perform
any operation on the variable directly.
int main()
{
int var1, var2; //two integer variables
int* ptr; //pointer to integers
ptr = &var1; //set pointer to address of var1
*ptr = 37; //same as var1=37
var2 = *ptr; //same as var2=var1
cout << var2 << endl; //verify var2 is 37
return 0;
}
• Remember that the asterisk used as the dereference operator has a different
meaning than the asterisk used to declare pointer variables.
• int* ptr; //declaration: pointer to int
• *ptr = 37; //indirection: value of variable pointed to by ptr
Introduction
• Here’s a summary of what we’ve learned so far:
• int v; //defines variable v of type int
• int* p; //defines p as a pointer to int
• p = &v; //assigns address of variable v to pointer p
• v = 3; //assigns 3 to v
• *p = 3; //also assigns 3 to v
Pointer to void
• We should note that the address that you put in a pointer must be the same
type as the pointer. You can’t assign the address of a float variable to a pointer
to int, for example:
• float flovar = 98.6;
• int* ptrint = &flovar; //ERROR: can’t assign float* to int*
• However, there is an exception to this. There is a sort of general-purpose pointer
that can point to any data type. This is called a pointer to void, and is defined like
this:
• void* ptr; //ptr can point to any data type
• Such pointers have certain specialized uses, such as passing pointers to functions
that operate independently of the data type pointed to.
Pointers and Arrays
• There is a close association between pointers and arrays; as previously discussed
in “Arrays and Strings”, how array elements are accessed. The following program
provides a review:
int main()
{ //array
int intarray[5] = { 31, 54, 77, 52, 93 };
for(int j=0; j<5; j++) //for each element,
cout << intarray[j] << endl; //print value
return 0;
}
• The cout statement prints each array element in turn.
• Surprisingly, array elements can be accessed using pointer notation as well as
array notation as can be seen in the following program.
Pointers and Arrays
int main()
{ //array
int intarray[5] = { 31, 54, 77, 52, 93 };
for(int j=0; j<5; j++) //for each element,
cout << *(intarray+j) << endl; //print value
return 0;
}
• The expression *(intarray+j) in the above code has exactly the same effect as intarray[j].
• Remember that the name of an array (i.e., intarray) is its address. The expression intarray+j is
thus an address with something added to it.
• e.g., intarray+3 interprets it as the address of the fourth integer in intarray, not the fourth byte.
• To retrieve the value of the fourth array element, not the address, we use the dereference
operator (*). The resulting expression, when j is 3, is *(intarray+3), which is the content of the
fourth array element, or 52.
new and delete Operators
• When a pointer is declared like this: float* p; // p is a pointer to a float
• p is uninitialized: it is not pointing to any allocated memory. Any attempt to access the memory to
which it points will be an error:
• *p = 3.14159; // ERROR: no storage has been allocated for *P
• A good way to avoid this problem is to initialize pointers when they are declared:
• float x = 3.14159; // x contains the value 3.14159
• float* p = &x; // p contains the address of x
• cout << *p; // OK: *p has been allocated
• Another way to avoid the problem of a dangling pointer is to allocate memory explicitly for the
pointer itself. This is done with the new operator:
• float* q;
• q = new float; // allocates storage for 1 float
• *q = 3.14159; // OK: *q has been allocated
• The new operator returns the address of a block of s unallocated bytes in memory, where s is the
size of a float. (Typically, sizeof(float) is 4 bytes.) Assigning that address to q guarantees that *q is
not currently in use by any other variables .
new and delete Operators
• The delete operator reverses the action of the new operator, returning allocated
memory to the free store. It should only be applied to pointers that have been
allocated explicitly by the new operator:
• float* q = new float(3.14159);
• delete q; // deallocates q
• *q = 2.71828; // ERROR: q has been deallocated
• Deallocating q returns the block of sizeof(float) bytes to the free store, making it
available for allocation to other objects.
• Once q has been deallocated, it should not be used again until after it has been
reallocated. A deallocated pointer, also called a dangling pointer, is like an
uninitialized pointer: it doesn’t point to anything.
Dynamic Arrays
• An array name is really just a constant pointer that is allocated at compile time:
• float a[20]; // a is a const pointer to a block of 20 floats
Here, a is constant pointers to blocks of 20 floats.
• The declaration of a is called static binding because it is allocated at compile time; the symbol is bound
to the allocated memory even if the array is never used while the program is running.
• In contrast, we can use a non-constant pointer to postpone the allocation of memory until the program
is running. This is generally called run-time binding or dynamic binding:
• float* p = new float[20];
• An array that is declared this way is called a dynamic array. Compare the two ways of defining an array:
• float a[20]; // static array
• float* p = new float[20]; // dynamic array
• The static array a is created at compile time; its memory remains allocated throughout the run of the
program. The dynamic array p is created at run time; its memory allocated only when its declaration
executes. Furthermore, the memory allocated to the array p is deallocated as soon as the delete
operator is invoked on it:
• delete [] p; // deallocates the array p
Pointers and Functions
• There are three ways to pass arguments to a function: by value, by reference, and by pointer.
• If the function is intended to modify variables in the calling program, these variables cannot be passed by value, since the
function obtains only a copy of the variable.
• However, either a reference argument or a pointer can be used in this situation.
• Let’s see how arguments are passed by reference, and then compare this to passing pointer arguments.
void centimize(double&); //prototype void centimize(double*); //prototype
int main(){ int main(){
double var = 10.0; //var has value of 10 inches double var = 10.0; //var has value of 10 inches
cout << “var = “ << var << “ inches” << endl; cout << “var = “ << var << “ inches” << endl;
centimize(var); //change var to centimeters centimize(&var); //change var to centimeters
cout << “var = “ << var << “ centimeters” << endl; cout << “var = “ << var << “ centimeters” << endl;
return 0;} return 0;
void centimize(double& v){ }
v *= 2.54; //v is the same as var void centimize(double* ptrd){
} *ptrd *= 2.54; //multiply the contents of ptrd by 2.54
}
• The function centimize() is declared as taking an argument that is a pointer to double. When main() calls the function, it
supplies the address of the variable as the argument. Because the centimize() function is passed an address, it must use the
dereference operator,*ptrd, to access the value stored at this address. Since ptrd contains the address of var, anything done to
*ptrd is actually done to var.
Passing Arrays
• Previously, we have passed array as arguments to functions but it’s more common to use pointer notation instead of
array notation when arrays are passed to functions.
#include <iostream>
• The prototype for the function is
using namespace std;
different. In array notation this is
void centimize(double*); //prototype
written as: void centimize(double[])
const int MAX = 5; //number of array elements
• Unary operator * and ++ have
int main() {
same precedence with right
double varray[MAX] = { 10.0, 43.1, 95.9, 59.7, 87.3 };
associativity.
centimize(varray); //change elements of varray to cm
• The pointer is incremented first
for(int j=0; j<MAX; j++) //display new array values
and the dereference operator is
cout << “varray[“ << j << “]=”<< varray[j] << “ centimeters” << endl;
applied to the resulting
return 0;
address.
}
void centimize(double* ptrd) {
for(int j=0; j<MAX; j++)
*ptrd++ *= 2.54; //ptrd points to elements of varray
}
Pointers to Pointers
• A pointer may point to another pointer. For example,
char c = 't';
char* pc = &c;
char** ppc = &pc;
char*** pppc = &ppc;
***pppc = 'w'; // changes value of c to 'w'
• We can visualize these variables like this:
• The assignment ***pppc = 'w' refers to the contents of the address pc that is pointed to by the
address ppc that is pointed to by the address pppc.

You might also like