9

Pointers and References

LEARNING OBJECTIVES

At the end of the chapter, you should be able to understand and use

  • Pointers and references, and know when to use what?

  • Objects and pointers.

  • Objects and references.

  • Dangling pointers and memory leaks.

  • Constant pointers, constant objects and references.

9.1 Introduction

In our experience of teaching C++, we found that learners are pretty confused about pointers and references and related memory management. This is mainly because they would have probably picked up the “what” of pointers and references and not the “why” of pointers and references. In this chapter, we would like to bring out special characteristics of pointers and references like

  • Pointers are just like any other variables.
  • Pointers have an address like a variable.
  • Pointers point to a variable or a structure like array or an object.
  • If you want a value of the object pointed by pointer just dereference the pointer.
  • Operator new allocates memory for the pointer and delete frees the memory.
  • The best part of pointers is that they can be reassigned.
  • References are another name for the object.
  • References always refer to the object.
  • References cannot be reassigned.

In this chapter, starting from basics we would teach you how to handle pointers and references for C++ paradigm, without pitfalls such as dangling pointers and memory leaks.

9.2 What, Why and How of Pointers

In C++, we would like to use pointers because they point to location in memory and are very efficient to move multiple data items between the main program and function as function arguments. In addition, you can have pointers to a function and execute different functions just by reassigning the pointer based on the user's choice. Pointers would facilitate reassignment just like you can point at any person with your index finger.

How does Arjuna, the famous archer in Mahabharata, use his arrows? Firstly, he removed an arrow from his storage. Secondly, he gave a name (mantra like Nag Astra as shown in Figure 9.1). Then he points it toward the ground while thinking or getting address from his guide (Lord Krishna). He then pointed it to the ground so that the arrow did not take off accidentally and hit passers by or unintended targets. Lastly, he aimed at the target at the address given before letting it go! We will also do the same in the case of pointers.

 

A pointer example with terms

 

Figure 9.1 A pointer example with terms

 

int *ptr=0; // you have created a pointer of type int
        // Note ptr is the pointer
        // Pointer is the address
        // *ptr is the value
        // you have now pointed to NULL (0 is treated as NULL)

When * precedes a variable in declaration statement, it means that the variable is a pointer variable. For example, ptr in int *ptr=0; is a pointer variable. It is a good safe programming practice to initialize the pointer as soon as it is created.

9.3 Pointers Declaration and Usage

Let us say that at address 2513 we have stored an integer variable called age as shown in Figure 9.2. At address 2613 we have integer variable age2.

 

Pointer in a memory

 

Figure 9.2 Pointer in a memory

 

Example 9.1:   Pointers Declaration and Usage


int age=50;
int age2=18;
 //we want pointer ptr to point to age;
 ptr = & age; // you have assigned ptr to age. & is called address operator
 cout<< age; // displays 50
 cout<<*ptr; // displays 50 . *ptr which is value stored location, i.e. 50
 //Once created you can reassign pointers
 ptr=& age2;
 cout<<*ptr; // displays 18 . *ptr which is value stored location, i.e. 18

Dereference or indirection operator ( *) is used to obtain the value pointed by the pointer. For example, cout<<*ptr; // displays 50

 

Example 9.2:    ptr1.cpp Pointers Usage


#include<iostream>
using namespace std;
void main()
{ int age1=50;
  int age2=18;
  //create a pointer
  int * ptr=0;
  // assign it to age1
  ptr = & age1; // & is address of operator
  cout<<”
 age1 “<< age1<<endl; // displays 50
  cout<<”
 age1 with *ptr “<< *ptr<<endl; // displays 50 .*ptr is value.
  cout<<”
 address using (&age1) and ptr : “<<&age1<<”	”<<ptr<<endl;
  // ptr is address of age1, so is &age1. Hence both must be same
  // now we will reassign the same pointer to age2
  ptr = & age2;
  cout<<”
 your age (*ptr) : “<<”	”<< *ptr; // displays 18 .*ptr is value stored location
  cout<<”
 address of (&age2) and ptr : “<<& age2<<”	”<<ptr<<endl;
  // ptr is address of age2 so is &age2. Hence both must be same.
  cout<<”
 address of(&ptr) :”<<”	”<< & ptr<<endl; // prints out address of variable
  // ptr. We are not interested in this address. It is just another address.
}//end of main
/*Output:
age1 50
age1 with *ptr 50
address using (&age1) and ptr : 0012FF7C 0012FF7C
your age (*ptr) : 18
address of (&age2) and ptr : 0012FF78 0012FF78
address of(&ptr) : 0012FF74

A note about address scheme of Intel processors is appropriate: When you ask for a display of address, the system would display a 32-bit address like fffffff4 in hexadecimal notation, which means

1111 1111 1111 1111 1111 1111 1111 0100 in binary.

9.4 Call by Value and Call by Reference (Pointers)

What are they? You can pass variables to a function by either of:

  1. Call by Value: The value of arguments is copied on to formal arguments whenever a function is called. Thus, there is an overhead of copying. As only copy reaches the function, the changes made in the local function are not reflected onto the original variable in the calling function. Further, if the data to be copied is large, ex. structure, the method is inefficient. It is hence used only for small data.
  2. Call by reference: Actual arguments are not copied but only addresses (pointers) are forwarded. The functions get these addresses as arguments and works on the variables located at the addresses forwarded. Hence, changes made to the variables are reflected on to variables from calling function. We are forwarding only addresses – there are no copying overheads as in call by value. Call by reference is of two types:
    1. Call by reference using pointers
    2. Call by reference using reference

Both types are effective. We will use pointers if reassigning is required, whereas if you use reference, its fixed memory location and reassignment to new location is NOT feasible.

 

Example 9.3:    valptrref. Program to Highlight Call by Value and Call by Reference Using Pointers

#include <iostream>
using namespace std;
// declaration of function prototypes
void Swap( int a , int b); // call by value
void PtrSwap ( int *a , int * b); // Call by Ref. a & b are pointers by def
void main()
  { int x=5;
  int y=10; 
  // call by value
  Swap( x,y);
  cout<<"
after call by value :"<<endl;
  cout<<"x value after swap:"<<x<<endl;
  cout<<"y value after swap:"<<y<<endl;
  // call by reference using pointers. Note that we have to send pointers
  //i.e. addresses of x & y . Hence we will pass &x , and & y.
  PtrSwap(&x,&y); 
  cout<<"
after call by ref using pointers :"<<endl;
  cout<<"x value after Ptrswap:"<<x<<endl;
  cout<<"y value after Ptrswap:"<<y<<endl;
}//end of main
// Function definitions
void Swap ( int a, int b)
 { int temp ; // two local variables
   temp=a;
   a=b;
   b=temp;
   cout<<"
inside Swap using pointers :"<<endl;
   cout<<"x value inside Swap:"<<a<<endl;
   cout<<"y value inside Swap:"<<b<<endl;
}
void PtrSwap ( int *a, int *b)
   { // a & b are pointers . Hence we need a pointer called temp
   int temp;
   temp=*a;
   *a=*b;
   *b=temp;
   cout<<"
inside PtrSwap using pointers :"<<endl;
   cout<<"x value inside Ptrswap:"<<*a<<endl;
   cout<<"y value inside Ptrswap:"<<*b<<endl;
}
/*Output :inside Swap using pointers :
x value inside Swap:10
y value inside Swap:5
after call by value :
x value after swap:5
y value after swap:10
inside PtrSwap using pointers :
x value inside Ptrswap:10
y value inside Ptrswap:5
after call by ref using pointers :
x value after Ptrswap:10
y value after Ptrswap:5 */

In Example 9.3, a few interesting results are given. Though inside Swap function values actually were interchanged, they were not reflected in the main program as discussed. Whereas in the case of PtrSwap, wherein we have passed pointers, the result of PtrSwap was reflected to main programs. This is in consonance with what we have learnt.

9.5 Dynamic Memory New and Delete Functions

We have learnt the memory management and mapping of C++ language in Chapter 3. In this section, we will learn tools and techniques to use heap memory, also called free space or dynamic memory, by pointers and references by using operators such as new and delete.

Dynamic memory, or heap memory as it is known, affords very large programs to be run on limited primary memory resource. For example, the real memory requirement is known only at run-time from the user, whereas the memory allocation normally takes place at compile time. Further, to execute several of the overloaded functions, the system compiles, loads and then runs. So if the user chooses at run time only one of the several functions loaded on to primary memory, the balance memory is wasted and hence we cannot solve problems requiring large memories. Dynamic memory solves this problem with new and delete operators. Allocate the heap memory with new operators and immediately after use, release the memory with delete operator. Thus, precious and limited memory is available for allocation and thus we can solve larger problems demanding larger memory. We can declare a heap variable using new operator.

int *x = new int; // creation and allocation of heap memory
*x=25; // assign value
Alternately, we can use a single statement
int *x = new int(12); allocate value 12 to pointer variable on heap memory.

Once allocated, you can use the dynamic memory pointer like an ordinary pointer. Remember that you have to release the memory after use so that the released memory can be used for other heap memory requirements. In this way, we can solve more complex problems because compiler loads a heap variable and allocates the exact memory requirements indicated by the user as well as frees the memory after use. It is good practice to delete the dynamic memory allocated using delete operator. In fact, the number of new declarations must match the number of deletes, though the system automatically releases the memory once the program ends.

int *x = new int(12); // created a pointer x on heap and allocated a value =12
*q *=*q ; // find the square of value, i.e. 144
cout<<*q;
……………
delete q; // q pointer has been deleted

9.5.1 Memory Leak

If you reallocate the dynamic pointer to a new variable without deleting the existing assignment, the originally assigned variable and heap memory allocated is permanently lost and not available to program. This is called memory leak.

 

Example 9.4:    Memory Leak

int *x = new int(12);
*q *=*q ; // find the square of value i.e. 144
  cout<<*q;
int *x = new int(75); // Error since reassigned to new without deleting
  delete q; // q pointer has been deleted.

9.5.2 Dangling Pointer

In this, the user tries to use the pointer after it has been deleted

 

Example 9.5:    Dangling Pointer: An Example

int *x = new int(12);
*q *=*q ; // find the square of value i.e. 144
 delete q; // q pointer has been deleted.
cout<<*q; // Error. q has become dangling pointer

9.5.3 Pointers and Arrays

Let us understand the connection between pointers and arrays:

Note that ‘x’ is the name of the array. ‘x’ is also the address of the array and as well as the address of the first element of the array. Hence, we can call ‘x’ as the pointer to the array too.

Suppose you want to print the 4th element, i.e. 40; as per C++ convention, you would write cout<<x[3]. Now as the element, we are interested in is at position 4 (3 in case we are counting from 0), i.e. at address x+3, we have learnt that if we want value from address we have to de-reference the address by using *.

                       Ex cout<< *(x+3);

Figure 9.3 shows array elements together with their addresses.

 

Array with addresses (pointers)

 

Figure 9.3 Array with addresses (pointers)

9.5.4 Pointers and Two-dimensional Arrays

Let us say that you have a two-dimensional array ‘a’ with 12 rows and 20 columns.

Then we can declare it as:

     int a[12][20]
         or

as a one-dimensional array using pointers. For example:

         int *a[20]

We can also depict the above a as pointer to pointer ** a

Pictorially following the figure makes the concept clear. Therefore, a[0] points to the beginning of the first row. a[11] points to the beginning of the 11th row. Note that a[0]….a[11] are pointers to respective rows.

 

image

 

Now suppose you want to access the 1st row 5 element; then a[1] is the pointer to the first row and 5 elements displacement is 5:

We know we can write a[1] as *(a+1)

Therefore, the address of the desired element is a[1]+5 or *(a+1) +5

The value of element is : * (*(a+1) + 5 ).

9.5.5 Array Declaration on Heap Memory

We can declare an array of 12 integers as : int x[12]. This array declaration reserves 12 contiguous locations in memory. In case the user does not use all 12 locations, memory will be wasted. Array declaration is an example of static memory allocation. Instead, we can also declare an array as

int *x =new int[12]; // x is a pointer variable pointing to array by name x
//, having 12 contiguous locations.
int *ptr=x; // now ptr points x[0], i.e. starting of an array

Let us write a program to pass an array to a function that receives an array by reference and sorts an integer array. We can define an array of 12 integers using new function and release the heap memory after use by deleting function, as follows:

 

Example 9.6:    dynarray.cpp A Program for Sorting of an Array by Passing an Array by Reference and to Find the Maximum of an Array

#include<iostream>
sing namespace std;
// function prototypes
template<class T>
T FindMax( T x[] , int n);
template<class T>
void SortArray( T *a , int n);
void main()
{ int n;
  int max; // n=no of values in an array
  int *x; // x is a pointer to an array
  cout<<"how many elements in your array :";
  cin>>n;
  // allocate dynamic memory space using new operator
  x=new int[n]; // allocates 12 contiguous location to pointer x
  // read in the array
  for (int i=0;i<n;i++)
   { cout<<"value for "<<i+1<<"element:";
        cin>>x[i];
}
cout<<("
 The entered array is….
");
for (i=0;i<n;i++)
  cout<<" "<<* (x+i); // same as writing x[i]
// call Findmax function
 max=FindMax(x,n);//x is a pointer to array
 cout<<"
 maximum value of given array="<<max<<endl;
// call SortArray function
 SortArray(x,n);
 cout<<("
 The entered array is….
");
 for (i=0;i<n;i++)
 cout<<" "<<x[i]; // same as writing *(x+i)
 // now that our work with array is over let us delete the memory allocated
 delete [] x; // memory allocated with new operator stands released
}//end of main
// Fn definition
template<class T>
T FindMax(T x[], int n)
 { T max;
   int i;
   max=T(); // *x is the value of 1 element
   for(i=1;i<n;i++)
   { if (max<*(x+i))
 max=*(x+i);
}
return max;
} // end of FindMax
template<class T>
void SortArray( T *a ,int n)
  { int i,j; // i for outer loop j for inner loop and temp for swapping
  T temp=T();
for ( i=0; i< n-1; i++) // last value need not be sorted
      {
          // find the smallest of remaining numbers and exchange it with
         for ( j=i+1; j< n; j++)
           { if (*(a+j) < *(a+i))
                { // swap
                  temp=*(a+i);
                  *(a+i)=*(a+j);
                  *(a+j)=temp;
                }
           }
    }
}
/*output : how many elements in your array :6
value for 1element:32
value for 2element:43
value for 3element:34
value for 4element:65
value for 5element:67
value for 6element:12
The entered array is….
32 43 34 65 67 12
maximum value of given array=67
The sorted array is….
12 32 34 43 65 67

9.5.6 Pointer to Pointer

You remember the treasure hunt game we all have played at some time or the other. In a first clue, we would receive a chit that gives clues regarding a second address at which the second clue or treasure is kept. Pointer to pointer can be viewed as pointer to an address, i.e. an address that points to another address. We can recover the value from the second address. For example, consider a two-dimensional matrix.

Let us say that you have a two-dimensional array ‘a’ with 12 rows and 20 columns.

Then we can declare it as:

                     int a[12][20]
                          or

as a one-dimensional array using pointers. For example:

                  int *a[20]

We have learnt in Section 9.4.4 that we can also depict the above a as pointer to pointer ** a. We have shown deployment of dynamic memory allocation techniques in Example 9.4.

When will the pointer to pointer be useful? An array or function is known by its name. We have also learnt by now that name is address. Symbol Address Table stores names and also address allocation for functions, arrays, variables, etc. Now in situations wherein there is a need to delete a first entry like the first element of the array or there is a need to add an element in the front, a normal pointer would entail changing of name in the symbol address table and entail in fructuous work. A pointer to pointer would isolate names and addresses stored by the system and hence adding in the front and deletion would become less cumbersome.

9.5.7 Dynamic Memory for a Two-dimensional Array

In our next example, we will demonstrate the creation and usage of dynamic memory using new operators. We will read a 2 x 2 matrix. First, we will allocate dynamic memory for rows and then we will allocate for columns of each row.

We will use try and catch blocks. This feature will be explained in detail in the chapter on errors and exceptions, but for now, we will introduce the concept in brief here. When an error occurs during the execution of a program, due to non-availability of resources, normally the program stops execution and reports the error or exception. Try and catch blocks provide a way out for the programmer to take corrective steps for the errors and exceptions that occur during run time. Try block will allocate heap memory during run time. If it fails due to nonavailability of memory or for any other reason, then it will throw the exception object. Catch block will catch the exception thrown by try block and take remedial measures.

 

Example 9.7:    matptrptr.cpp A Program for Reading and Printing of Matrix with Dynamic Memory Allocation Using Pointer to Pointer

#include<iostream>
using namespace std;
// functional prototype declarations
void ReadMatrix( int **A,int m , int n);
void PrintMatrix( int **A , int m , int n);
void main()
{ int m,n;
  int **A=0; // A is a pointer to pointer
  cout<<”Enter the order of matrix <m,n> :”;
  cin>>m>>n;
  /* Now allocate dyn memory. First allocate to rows
  Then allocate to columns. We will use try and catch blocks.
  Try will allocate dyn memory. If it fails due to non-availability of memory then it will throw the exception object.
  Catch block will catch the exception thrown by try block and take
  remedial measures. */
try
  {   A=new int * [m]; // dynamic memory for row allocation
   for (int i=0;i<m;i++)
    A[i]=new int[n]; // dynamic memory for columns
}
catch (bad_alloc)
 {         cout<<”
 bad allocation”<<endl;
       exit(1);
}
    ReadMatrix(A,m,n);
    printf(“The elements of the Matrix are:
”);
    PrintMatrix(A,m,n);
}       /*end of main*/
void ReadMatrix( int **A, int m,int n)
{       int i,j;
    cout<<”Enter the elements :”;
    for(i=0;i<m;i++)
     { for(j=0;j<n;j++)
           cin>>A[i][j]; /*input elements*/
 }
}//end of ReadMatrix
void PrintMatrix( int **A, int m,int n)
{    int i,j;
 for(i=0;i<m;i++)
 {  for(j=0;j<n;j++)
        cout<<” “<<A[i][j];
    cout<<endl;
 }
}
/*output: Enter the order of matrix <m,n> :2 2
Enter the elements :1 2 3 4
The elements of the Matrix are:
 1 2
 3 4*/

9.5.8 Pointers and Three-dimensional Arrays

To access an element a[3][4][5]

  1. a is the pointer to the first row. We need three rows. Therefore, it is a[3] or *(a+3).
  2. Column displacement is 4. Therefore, the address is *(a+3)+4 and the value is: *(*(a+3)+4).
  3. Three-dimensional displacement is 5. Therefore, the address is: *(*(a+3)+4). The value of element, therefore, is: *(*(*(a+3)+4)).

9.5.9 Array of Pointers

Pointers can be stored in arrays. You already know that pointer means address, hence an array of pointers means a collection of addresses. For example, you can store a set of five pointers each pointing to a string variable like:

char * ptr[5] = { “welcome”, “to” ,”self_learning”,”C++” , ”Book” }

ptr is a dimension 5, i.e. an array of five pointers. The following example will make the concept clear:

 

Example 9.8:    arrayofptr.cpp Program to Demonstrate Use of Array of Pointers

#include<iostream>
using namespace std;
void main()
 {  // array of pointers with 5 elements
char *ptr[5]={“welcome”,”to”,”self_learning”,”C++A”,”Books”};
char *x; // x is a pointer of type char
x=ptr[0]; // x now points to ptr, i.e. starting pointer in an array of pointers
for ( int i = 0 ; i< 5; i++)
    cout<<”
”<< *(ptr+i)<<endl;
// following cout statements will teach you more about array of pointers
 cout<<”
”<< *ptr[3]; // you can expect value of starting element in CDS i.e. C
}
/*Output:
welcome
to
self_learning
C++
Books*/

9.5.10 Pointers to Void

Remember ‘void’ is a data type. Usually, we will declare pointer to point to a particular type of data. For example, int * ptr or char * ptr, etc. The void can be employed if your program has multiple data types. But typecasting is essential, when the void pointer is used as shown in Figure 9.4.

 

Void pointer declaration, definition and reassignment

 

Figure 9.4 Void pointer declaration, definition and reassignment

Declaration:

int x;
float salary;
void *ptr ; // pointer to void data type declaration

Definition:

ptr = & x ; // assign pointer to type integer

Usage:

Type casting the void pointer is mandatory

cout<<*(int*)ptr;

Reassignment:

ptr=&sal;
cout<< * (float*)ptr; // typecasting of ptr to float

 

Example 9.9:    voidptr.cpp A Program to Demonstrate the Use of Void Pointers

#include<iostream>
using namespace std;
#include<iostream>
using namespace std;
void main()
{  int x=100;
   float sal=2000.00;
   void * ptr;// ptr is a pointer to data type void
   // assign void pointer to int
   ptr=&x;
   cout<<”
ptr type casted to integer :”<< *(int*)ptr; // typecasting of ptr to int
   // assign void pointer to float
   ptr=&sal;
   cout<<”
ptr type casted to float :”<<*(float*)ptr; // typecasting of ptr to float
}//end of program
/*Output : ptr type casted to integer :100
ptr type casted to float :2000*/

9.5.11 Pointer to a Constant vs const Pointer

We are aware that pointers are addresses and provide us a fast access to memory and data manipulation. But there are issues of safe operation and data integrity. There are situations wherein we want that data should not be altered. In such cases, we would have declared that data as constant. The main advantage of pointers is that they can be reassigned. This feature allows us to navigate through the array or memory, etc. But there are situations when we need the pointers to be constant, i.e. they should not be reassigned. In such cases, we would declare const pointers.

Thus, there are two combinations involved, i.e. pointer type and data type. Each type can have two variations of data type: constant and normal. Therefore, we can have a total of four variations in the declaration of variables. These are shown below:

For example, let us say we have two variables declared as

int val = 25;
const float PI=3.14159;

Pointer to data // normal pointer and normal data

int *x=& val;
Pointer to const data
// y is a pointer to constant float. It cannot be changed const float * y=&PI;
Const pointer to data
// constant pointer to int data type. Pointer cannot be reassigned int * const x=& val;
Const pointer to a const data
// constant pointer to constant data type. Hence value cannot be changed
// and pointer cannot be reassigned const float * const z=& PI

 

Example 9.10:    Pointers Constant Data and Constant Pointers. Constructors and Destructors of a Class

// pointer to int
int val = 25;                            // int variable
int *ptr = & val ;                // ptr is a normal pointer
cout<<++(*ptr);    // output 26
cout<<++ptr;       // ptr is incremented
// constant pointer to int
int const * cptr = & val; // cptr is constant pointer to int
cout<<++(*cptr);   // Allowed . output 26
cout<<++cptr;      // Not allowed . cptr cannot be incremented
// pointer to constant float PI
const float PI=3.14159;                  // float variable PI declared as constant
const float * cfptr = &PI;        // cfptr is pointer to const data type float
cout<<++(*cfptr);  // Not Allowed as *cfptr is constant
cout<<++cfptr;     // Allowed . Increments cfptr
// constant pointer to constant float PI
const float * const cfptc = & PI ; // cfptr is const pointer to const data
cout<<++(*cfptr);           // Not Allowed as *cfptr is constant
cout<<++cfptr;              // Not Allowed . cfptr is constant.

9.5.12 Pointers to Function

We are aware a name means an address. Like array, name is address of the array. Hence, we can call it as pointer to an array. Function name likewise is an address. We can think of it as a pointer to a constant. At that address, the code for the function is stored.

Pointer to function hence means pointer to a constant pointer.

int Findmax( int a[] , int n); // A function is declared
int (*ptrfun ) (int) ; // a pointer to function i.e. ptrfun is created
ptrfun = & FindMax ; // pointer to function is assigned to FindMax function

Note that in the statement int (*ptrfun ) (int) ; the function takes int as argument and returns int data type.

 

Example 9.11:    ptrtofun.cpp. Program to Demonstrate Use of Pointers to Function

#include<iostream>
using namespace std;
void FindTriangle(float &a , float & b );
void FindRect(float &a , float & b );
void (*funptr) ( float & , float & ); // ptr to function
void main()
{ float x= 25.0 , y=50.0;
  int choice;
  cout<<"
 Enter your choice .. < 0 to quit : 1 for Triangle : 2 for Rectangle >";
  cin>>choice;
  while ( choice !=0)
  {    switch (choice)
   case 0 : cout<<"
 exiting the programme…."<<endl;
            exit(0);
   case 1 : funptr = FindTriangle; break;
   case 2 : funptr=FindRect; break;
 }// end of switch
 funptr(x,y);
  cout<<"
 Enter your choice .. < 0 to quit : 1 for Triangle : 2 for Rectangle >";
  cin>>choice;
}//end of while
}//end of main
//Fn definitions
void FindTriangle( float &a , float &b)
{cout<<"
 Area of the Triangle = "<< 0.5*a*b << endl;}
void FindRect( float &a , float &b)
{cout<<"
 Area of the Rectangle = "<< a*b << endl;}
/*Output : Enter your choice .. < 0 to quit : 1 for Triangle : 2 for Rectangle >1
Area of the Triangle = 625
Enter your choice .. < 0 to quit : 1 for Triangle : 2 for Rectangle >2
Area of the Rectangle = 1250
Enter your choice .. < 0 to quit : 1 for Triangle : 2 for Rectangle >0*/

9.6 What, Why and How of References

C++ gives us an additional facility called reference. A reference is an alias or another name to a variable. Figure 9.5 shows usage of reference.

 

Reference x and rx refer to the same location 2513 and value 25

 

Figure 9.5 Reference x and rx refer to the same location 2513 and value 25

 

Reference operator &: It has two uses

&x ; // evaluates the address of x
int & rx = x ; implies that rx is a reference of type int

 

 

Example 9.12:    ptrtofun.cpp. 6 ref1.cpp Program to Introduce Reference Concepts

#include<iostream>
using namespace std;
void main()
{   int age1=50;
int age2=18;
//create a reference to age1 i.e. an alias by name rage to age1
int & rage=age1; //rage is an alias or another name to age1
cout<<”
 age1 “<< age1<<”	”<<”&age1 “<< &age1<<endl; // displays address of age1
cout<<”
 age1 with rage”<< rage<<”	”<<”&rage “<< &rage<<endl;
cout<<”
 both rage and age1 must have same addresses..”<<endl;
/* you cannot reassign references like you did for pointers. If you do, the original assigned data will be lost*
rage = age2;
cout<<”
 wehave forcibly reassigned a reference to “;
cout<<”
 another variable. Rage is an alias of age1.”;
cout<<”
 Therefore age1 will now have value of age2.”<<endl;
cout<<”
 age1 “<< age1<<”	”<<”&age1 “<< &age1<<endl;
cout<<”
 age2 “<< age2<<”	”<<”&age2 “<< &age2<<endl;
cout<<”
 rage assigned to age2 but value it hold is age1 :”<<rage<<endl;
}//end of main
/*Output : age1 50 &age1 0012FF7C
age1 with rage50 &rage 0012FF7C
both rage and age1 must have same addresses..
we have forcibly reassigned a reference to
another variable. Rage is an alias of age1.
Therefore age1 will now have value of age2.
age1 18 &age1 0012FF7C
age2 18 &age2 0012FF78
rage assigned to age2 but value it holds is age1 :18*/

9.6.1 Which is Better – Pointer or Reference?

Pointers are used when you have to reassign. For example, if you are using an array that stores values at contiguous memory locations, you will need to increment the pointer to refer to the next element of the array. But pointers, though quick acting , have inherent dangers associated with them like memory leak, memory crash, dangling pointers, etc.

References are aliases – another name for variables. They cannot be reassigned. If you insist and reassign, as reference is an alias, the first variable will be assigned with reassigned variables value.

Common advantages of both pointers and references. Both forward only addresses to function, i.e. they employ pass by reference technique. Hence, there are no overhead like making copies of variables and objects while passing these items to a function. Function can return more than one value.

References offer a clean efficient and risk and error-free environment and hence it is the experienced programmers first choice. We will present the advanced concepts involved with references through a series of programs in our chapter on objects. References offer risk-free and efficient programming. To start with, recall how PtrSwap program in Example 9.2 ensured that changes made in function reflected in the main program. In our next example, we have shown how we can achieve the same result by using pass by reference.

 

Example 9.13:    RefSwap.cpp. A Program to Swap Values by Using References Concepts

#include<iostream>
using namespace std;
// declaration of function prototypes
void RefSwap ( int &a , int & b); // Call by Ref. a & b are pointers by def.
void main()
{  int x=5, y=10;
   // call by reference using reference. Note that we have to send reference
   //In prototype we have promised to send references like &a and &b
   //i.e. addresses of x & y . Hence we will pass x and y.
   RefSwap(x,y);
   cout<<”
after call by ref using References :”<<endl;
   cout<<”x value after RefSwap:”<<x<<endl;
   cout<<”y value after RefSwap:”<<y<<endl;
}//end of main
// Function definitions
void RefSwap ( int &a, int &b)
{ // a & b are references . Hence we need ordinary variable temp
  int temp; temp=a; a=b; b=temp;
  cout<<”
inside RefSwap using reference :”<<endl;
  cout<<”x value inside RefSwap:”<<a<<endl;
  cout<<”y value inside RefSwap:”<<b<<endl;
}
/*Output:
inside RefSwap using reference :
x value inside RefSwap:10
y value inside RefSwap:5
after call by ref using References :
x value after RefSwap:10
y value after RefSwap:5 */

 

Example 9.14:    RefSwap.cpp. A Program to Swap Values by Using References Concepts

In this example, we will show how to return a reference from a function FindLarge. We declare a function prototype as double & FindLarge(double &r, double &s).

The function finds the largest of the two double quantity and returns a reference to largest, in this case to m. Also observe the working of FindLarge (k, m)=10 ; & FindLarge (k, m) ++; statements.

#include <iostream>
using namespace std;
double & FindLarge(double &r, double &s);
int main ()
{ double k=5, m=9;
  cout <<" Given Values k: " << k <<" : m: " << m << endl;
  cout <<"
Values after FindLarge (k, m) : "<<FindLarge (k, m)<<endl;
  cout <<" Values k: " << k <<" : m: " << m << endl; //output 9
  cout << endl;
  FindLarge (k, m)=10; // largest is m=9 ,it is replaced by 10 .
  cout <<"
Values after FindLarge (k, m) = 10"<<endl;
  cout <<" Values k: " << k <<" : m: " << m << endl; //output 3 & 10
  cout << endl;
  FindLarge (k, m) ++;
  cout <<"
Values after FindLarge (k, m)++"<<endl;
  cout <<" Values k: " << k <<" : m: " << m << endl; //output 3 & 11
  cout << endl;
  return 0;
}
double & FindLarge(double &r, double &s)
{if (r > s) return r;
  else return s;
}
/*output :Given Values k: 5 : m: 9
Values after FindLarge (k, m): 9
 Values k: 5 : m: 9
Values after FindLarge (k, m)=10
 Values k: 5 : m: 10
Values after FindLarge (k, m)++
 Values k: 5 : m: 11 :*/

9.7 Summary

  1. Pointers are like variables. Hence, they have address.
  2. Pointers are addresses.
  3. To get a value, use dereference operator * on pointer.
  4. Pointers can be reassigned to point to any other variable of the same data type.
  5. Operator new allocates space for pointer and statement delete frees the dynamic pointer.
  6. Memory leak is reallocating the dynamic pointer to a new variable without deleting the existing assignment; the originally assigned variable and heap memory allocated is permanently lost and not available to program.
  7. Dangling pointer: In this, the user tries to use the pointer after it has been deleted.
  8. Array of pointers. Pointers can be stored in arrays. Array of pointers means collection of addresses.
  9. Void pointers point to data type called void. Explicit type casting is required when void pointers point to a particular type of data.
  10. Pointer to function. It is as a pointer to a constant pointer as function name is an address. At that address, the code for the function is stored.
  11. A reference is an alias or another name to a variable. A reference always points to a variable.
  12. A reference cannot be reassigned. If reassigned, the original variable will lose its data.
  13. A reference is risk free and free from memory leaks and dangling pointers.
  14. A reference is best suited when reassignments are not present or when we need fixed address.
  15. A pointer, on other hand, is most suited when reassignment is required.

Exercise Questions

Objective Questions

  1. The pointer is used to specify a pointer whose base type is unknown and is a generic pointer.
    1. NULL pointer
    2. NIL pointer
    3. 0
    4. Void
  2. ___________ is the means by which a program can obtain memory during run time.
    1. Static allocation
    2. Dynamic allocation
    3. Stack allocation
    4. All of a, b, c.
  3. Memory allocated by C++'s dynamic allocation functions is obtained from ______memory.
    1. Global
    2. Static
    3. Stack
    4. Heap
  4. The new function returns a pointer of type __________ which means that we can assign it to any type of pointer.
    1. NULL pointer
    2. Void
    3. 0
    4. NIL
  5. In call by reference, actual arguments are not copied but only addresses (pointers) are forwarded.     True/False
  6. The declaration of two-dimensional arrays int a[12][20] and int *a[20] in dynamic memory allocation scheme are one and the same.     True/False
  7. Multiplication and division operations are allowed on pointers.     True/False
  8. Only addition and subtraction operations are permitted on pointers.     True/False
  9. The ____ is a unary operator that returns the memory address of its operand.
    1. &
    2. +
    3. %
    4. *
  10. Which among these is the indirection operator?
    1. &
    2. +
    3. %
    4. *
  11. Dereferenced means get the value stored at location.     True/False
  12. Indirection operator and dereferencing mean one and the same.     True/False
  13. Const pointer can call a non-constant function.     True/False
  14. If object is declared constant, then this pointer is a constant pointer.     True/False
  15. Dynamic memory can be allocated without the use of pointers.     True/False
  16. If int A[6] is a one-dimensional array of integers, which of the following refers to the value of fourth element in the array:
    1. * (A+4)
    2. * (A+3)
    3. A+4
    4. A+3
  17. cout<< x[5]; is equal to
    1. cout<< *x[5];
    2. cout<< x+5;
    3. cout<< *x+5;
    4. cout<< *(x+5);
  18. Constant this pointer can call non-constant functions.     True/False
  19. Pointer to a function is pointer to a constant pointer     True/False
  20. If ( a= =b) then ( &a = = & b)     True/False
  21. If ( a= =b) then ( *a= =*b)    True/False

    Short-answer Questions

  22. What are pointers? List out reasons for using pointers.
  23. Explain the process of assessing a variable through its pointer. Give an example.
  24. How to use pointers as arguments in a function? Explain through an example.
  25. Explain the process of declaring and initializing pointers. Give an example.
  26. Distinguish pointer * operator (indirection operator) and address operator(&) with examples.
  27. Explain passing by reference using pointers and using reference.
  28. Which usage is better: pointers or reference? Why?
  29. Distinguish & operator and reference in C++ language.
  30. Differentiate array of pointers and pointer to an array.
  31. Explain heap memory space or free storage space.
  32. Explain dynamic memory operators new and delete.
  33. How do you declare an array on heap memory? Explain with examples.
  34. How do you allocate dynamic memory for a two-dimensional array using pointer to pointer concept.
  35. Explain how a void pointer could be useful.
  36. Explain pointer to a function with examples.
  37. Distinguish dangling pointer and memory leak with examples.
  38. Explain different ways to use & operator.
  39. Distinguish reference operator and dereference operator.
  40. Explain pointer to pointer declaration in case of two-dimensional array.

    Long-answer Questions

  41. Write a cpp that forwards an array of strings of names function that return a new array that contains pointers to duplicate names.
  42. Write a cpp that reverses an array of integers.
  43. Write a cpp to find the trace of a square matrix. [Hint: Trace is sum of diagonal elements.]
  44. Write a cpp to find if the given matrix is singular matrix. [Hint: Matrix is singular if determinant is 0. Use recursion to find the determinant of a matrix.]
  45. Explain how dynamic memory can be allocated to two-dimensional array when the dimensions are not known at compile time. [Hint: Use pointer to pointer concept.]
  46. The declarations int a[5][6] ans *a [6] and **a all refer to the same two-dimensional array of integers. Explain the process of memory allocation for the above three declarations.
  47. Write a cpp to implement Merge Sort.

Solutions to Objective Questions

  1. d
  2. b
  3. d
  4. b
  5. True
  6. True
  7. False
  8. True
  9. a
  10. d
  11. True
  12. True
  13. False
  14. True
  15. False
  16. a
  17. d
  18. False
  19. True
  20. True
  21. False
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.41.229